/*========================================================================= Program: Visualization Toolkit Module: vtkOpenGLRenderer.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 "vtkOpenGLRenderer.h" #include "vtkCuller.h" #include "vtkHiddenLineRemovalPass.h" #include "vtkLightCollection.h" #include "vtkNew.h" #include "vtkObjectFactory.h" #include "vtkOpenGLCamera.h" #include "vtkOpenGLLight.h" #include "vtkOpenGLProperty.h" #include "vtkRenderWindow.h" #include "vtkOpenGLRenderWindow.h" #include "vtkOpenGLExtensionManager.h" #include "vtkgl.h" // vtkgl namespace #include "vtkImageImport.h" #include "vtkPNGWriter.h" #include "vtkOpenGLTexture.h" #include "vtkTimerLog.h" #include "vtkRenderPass.h" #include "vtkRenderState.h" #include "vtkOpenGL.h" #include "vtkOpenGLError.h" #include "vtkShaderProgram2.h" #include #include #include class vtkGLPickInfo { public: GLuint* PickBuffer; GLuint PickedId; GLuint NumPicked; }; vtkStandardNewMacro(vtkOpenGLRenderer); vtkCxxSetObjectMacro(vtkOpenGLRenderer,ShaderProgram,vtkShaderProgram2); #define VTK_MAX_LIGHTS 8 // List of rgba layers, id are 2D rectangle texture Ids. class vtkOpenGLRendererLayerList { public: std::list List; }; extern const char *vtkOpenGLRenderer_PeelingFS; vtkOpenGLRenderer::vtkOpenGLRenderer() { this->PickInfo = new vtkGLPickInfo; this->NumberOfLightsBound = 0; this->PickInfo->PickBuffer = 0; this->PickInfo->PickedId = 0; this->PickInfo->NumPicked = 0; this->PickedZ = 0; this->DepthPeelingIsSupported=0; this->DepthPeelingIsSupportedChecked=0; this->LayerList=0; this->OpaqueLayerZ=0; this->TransparentLayerZ=0; this->ProgramShader=0; this->DepthFormat=0; this->DepthPeelingHigherLayer=0; this->ShaderProgram=0; this->BackgroundTexture = 0; } // Internal method temporarily removes lights before reloading them // into graphics pipeline. void vtkOpenGLRenderer::ClearLights (void) { vtkOpenGLClearErrorMacro(); short curLight; float Info[4]; // define a lighting model and set up the ambient light. // use index 11 for the heck of it. Doesn't matter except for 0. // update the ambient light Info[0] = this->Ambient[0]; Info[1] = this->Ambient[1]; Info[2] = this->Ambient[2]; Info[3] = 1.0; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, Info); if ( this->TwoSidedLighting ) { glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); } else { glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0); } // now delete all the old lights for (curLight = GL_LIGHT0; curLight < GL_LIGHT0 + VTK_MAX_LIGHTS; curLight++) { glDisable(static_cast(curLight)); } this->NumberOfLightsBound = 0; vtkOpenGLCheckErrorMacro("failed ater ClearLights"); } // Ask lights to load themselves into graphics pipeline. int vtkOpenGLRenderer::UpdateLights () { vtkOpenGLClearErrorMacro(); vtkLight *light; short curLight; float status; int count; // Check if a light is on. If not then make a new light. count = 0; curLight= this->NumberOfLightsBound + GL_LIGHT0; vtkCollectionSimpleIterator sit; for(this->Lights->InitTraversal(sit); (light = this->Lights->GetNextLight(sit)); ) { status = light->GetSwitch(); if ((status > 0.0)&& (curLight < (GL_LIGHT0+VTK_MAX_LIGHTS))) { curLight++; count++; } } if( !count ) { vtkDebugMacro(<<"No lights are on, creating one."); this->CreateLight(); } count = 0; curLight= this->NumberOfLightsBound + GL_LIGHT0; // set the matrix mode for lighting. ident matrix on viewing stack glMatrixMode(GL_MODELVIEW); glPushMatrix(); for(this->Lights->InitTraversal(sit); (light = this->Lights->GetNextLight(sit)); ) { status = light->GetSwitch(); // if the light is on then define it and bind it. // also make sure we still have room. if ((status > 0.0)&& (curLight < (GL_LIGHT0+VTK_MAX_LIGHTS))) { light->Render(this,curLight); glEnable(static_cast(curLight)); // increment the current light by one curLight++; count++; } } this->NumberOfLightsBound = curLight - GL_LIGHT0; glPopMatrix(); glEnable(GL_LIGHTING); vtkOpenGLCheckErrorMacro("failed after UpdateLights"); return count; } // ---------------------------------------------------------------------------- // Description: // Access to the OpenGL program shader uniform variable "useTexture" from the // vtkOpenGLProperty or vtkOpenGLTexture. int vtkOpenGLRenderer::GetUseTextureUniformVariable() { GLint result=vtkgl::GetUniformLocation(this->ProgramShader,"useTexture"); vtkOpenGLCheckErrorMacro("failed at glGetUniformLocation"); if(result==-1) { vtkErrorMacro(<<"useTexture is not a uniform variable"); } return result; } // ---------------------------------------------------------------------------- // Description: // Access to the OpenGL program shader uniform variable "texture" from the // vtkOpenGLProperty or vtkOpenGLTexture. int vtkOpenGLRenderer::GetTextureUniformVariable() { GLint result=vtkgl::GetUniformLocation(this->ProgramShader,"texture"); vtkOpenGLCheckErrorMacro("failed at glGetUniformLocation"); if(result==-1) { vtkErrorMacro(<<"texture is not a uniform variable"); } return result; } // ---------------------------------------------------------------------------- // Description: // Is rendering at translucent geometry stage using depth peeling and // rendering a layer other than the first one? (Boolean value) // If so, the uniform variables UseTexture and Texture can be set. // (Used by vtkOpenGLProperty or vtkOpenGLTexture) int vtkOpenGLRenderer::GetDepthPeelingHigherLayer() { return this->DepthPeelingHigherLayer; } // ---------------------------------------------------------------------------- // Concrete open gl render method. void vtkOpenGLRenderer::DeviceRender(void) { vtkTimerLog::MarkStartEvent("OpenGL Dev Render"); if(this->Pass!=0) { vtkRenderState s(this); s.SetPropArrayAndCount(this->PropArray, this->PropArrayCount); s.SetFrameBuffer(0); this->Pass->Render(&s); } else { // Do not remove this MakeCurrent! Due to Start / End methods on // some objects which get executed during a pipeline update, // other windows might get rendered since the last time // a MakeCurrent was called. this->RenderWindow->MakeCurrent(); vtkOpenGLClearErrorMacro(); // standard render method this->ClearLights(); this->UpdateCamera(); this->UpdateLightGeometry(); this->UpdateLights(); // set matrix mode for actors glMatrixMode(GL_MODELVIEW); this->UpdateGeometry(); // clean up the model view matrix set up by the camera glMatrixMode(GL_MODELVIEW); GLint mvDepth; glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &mvDepth); if (mvDepth>1) { glPopMatrix(); } vtkOpenGLCheckErrorMacro("failed after DeviceRender"); } vtkTimerLog::MarkEndEvent("OpenGL Dev Render"); } // ---------------------------------------------------------------------------- void vtkOpenGLRenderer::DeviceRenderOpaqueGeometry() { bool useHLR = this->UseHiddenLineRemoval && vtkHiddenLineRemovalPass::WireframePropsExist(this->PropArray, this->PropArrayCount); if (useHLR) { vtkNew hlrPass; vtkRenderState s(this); s.SetPropArrayAndCount(this->PropArray, this->PropArrayCount); s.SetFrameBuffer(0); hlrPass->Render(&s); this->NumberOfPropsRendered += hlrPass->GetNumberOfRenderedProps(); } else { this->Superclass::DeviceRenderOpaqueGeometry(); } } // ---------------------------------------------------------------------------- // Description: // Render translucent polygonal geometry. Default implementation just call // UpdateTranslucentPolygonalGeometry(). // Subclasses of vtkRenderer that can deal with depth peeling must // override this method. void vtkOpenGLRenderer::DeviceRenderTranslucentPolygonalGeometry() { vtkOpenGLClearErrorMacro(); vtkOpenGLExtensionManager *extensions = NULL; if(this->UseDepthPeeling) { vtkOpenGLRenderWindow *context = vtkOpenGLRenderWindow::SafeDownCast(this->RenderWindow); if (!context) { vtkErrorMacro("OpenGL render window is required.") return; } extensions = context->GetExtensionManager(); if(!this->DepthPeelingIsSupportedChecked) { this->DepthPeelingIsSupportedChecked=1; int supports_GL_1_3=extensions->ExtensionSupported("GL_VERSION_1_3"); int supports_GL_1_4=extensions->ExtensionSupported("GL_VERSION_1_4"); int supports_GL_1_5=extensions->ExtensionSupported("GL_VERSION_1_5"); int supports_GL_2_0=extensions->ExtensionSupported("GL_VERSION_2_0"); int supports_vertex_shader; int supports_fragment_shader; int supports_shader_objects; if(supports_GL_2_0) { supports_vertex_shader=1; supports_fragment_shader=1; supports_shader_objects=1; } else { supports_vertex_shader=extensions->ExtensionSupported("GL_ARB_vertex_shader"); supports_fragment_shader=extensions->ExtensionSupported("GL_ARB_fragment_shader"); supports_shader_objects=extensions->ExtensionSupported("GL_ARB_shader_objects"); } int supports_multitexture=supports_GL_1_3 || extensions->ExtensionSupported("GL_ARB_multitexture"); int supports_occlusion_query; int supports_shadow_funcs; if(supports_GL_1_5) { supports_occlusion_query=1; supports_shadow_funcs=1; } else { supports_occlusion_query=extensions->ExtensionSupported("GL_ARB_occlusion_query"); supports_shadow_funcs=extensions->ExtensionSupported("GL_EXT_shadow_funcs"); } int supports_depth_texture; int supports_shadow; int supports_blend_func_separate; if(supports_GL_1_4) { supports_depth_texture=1; supports_blend_func_separate=1; supports_shadow=1; } else { supports_depth_texture=extensions->ExtensionSupported("GL_ARB_depth_texture"); supports_shadow=extensions->ExtensionSupported("GL_ARB_shadow"); supports_blend_func_separate=extensions->ExtensionSupported("GL_EXT_blend_func_separate"); } int supports_GL_ARB_texture_rectangle=extensions->ExtensionSupported("GL_ARB_texture_rectangle"); // spec claims it is GL_SGIS_texture_edge_clamp, reality shows it is // GL_EXT_texture_edge_clamp on Nvidia. // part of OpenGL 1.2 core // there is no new function with this extension, we don't need to load // it. int supports_edge_clamp=extensions->ExtensionSupported("GL_VERSION_1_2"); if(!supports_edge_clamp) { supports_edge_clamp=extensions->ExtensionSupported("GL_SGIS_texture_edge_clamp"); if(!supports_edge_clamp) { // nvidia cards. supports_edge_clamp=extensions->ExtensionSupported("GL_EXT_texture_edge_clamp"); } } GLint alphaBits; glGetIntegerv(GL_ALPHA_BITS, &alphaBits); int supportsAtLeast8AlphaBits=alphaBits>=8; // Mesa with "Offscreen" renderer (ie OS Mesa) supports depth peeling in // all versions tested 7.10, 8.0, 8.0.5, 9.0.3, 9.1.3, 9.1.4, 9.1.5 // Mesa version 7 with "Software Rasterizer" renderer all Opacity/Translucent // ctests fail with depth peeling // Mesa 9.2.0 with Gallium llvmpipe renderer some of these tests fail // Mesa 8 with GMA945 renderer supports depth peeling // ATI Radeon HD XXXXX on Windows chokes on PROXY_TEXTURE_RECTANGLE_ARB // memory querries however if those are not used all the tests pass. // ATI Radeon HD on Mac OS X PROXY_TEXTURE_RECTANGLE_ARB are fine but // TestTranslucentLUTDepthPeeling fails. So leave it disabled on Apple int driver_support = (!(extensions->DriverIsATI() && (extensions->GetDriverGLVersionMajor() < 3)) && (!extensions->DriverIsMesa() || extensions->DriverGLRendererHas("Offscreen") || (extensions->DriverVersionAtLeast(6,5,3) && !extensions->DriverGLRendererIs("Software Rasterizer") && !extensions->DriverGLRendererHasToken("llvmpipe")))) || extensions->GetIgnoreDriverBugs("ATI/Mesa depth peeling bug."); this->DepthPeelingIsSupported = supports_depth_texture && supports_shadow && supports_blend_func_separate && supports_shadow_funcs && supports_vertex_shader && supports_fragment_shader && supports_shader_objects && supports_occlusion_query && supports_multitexture && supports_GL_ARB_texture_rectangle && supports_edge_clamp && supportsAtLeast8AlphaBits && driver_support; if(this->DepthPeelingIsSupported) { vtkDebugMacro("depth peeling supported"); if(supports_GL_1_3) { extensions->LoadExtension("GL_VERSION_1_3"); } else { extensions->LoadCorePromotedExtension("GL_ARB_multitexture"); } // GL_ARB_depth_texture, GL_ARB_shadow and GL_EXT_shadow_funcs // don't introduce new functions. if(supports_GL_1_4) { extensions->LoadExtension("GL_VERSION_1_4"); } else { extensions->LoadCorePromotedExtension("GL_EXT_blend_func_separate"); } if(supports_GL_2_0) { extensions->LoadExtension("GL_VERSION_2_0"); } else { extensions->LoadCorePromotedExtension("GL_ARB_vertex_shader"); extensions->LoadCorePromotedExtension("GL_ARB_fragment_shader"); extensions->LoadCorePromotedExtension("GL_ARB_shader_objects"); } if(supports_GL_1_5) { extensions->LoadExtension("GL_VERSION_1_5"); } else { extensions->LoadCorePromotedExtension("GL_ARB_occlusion_query"); } extensions->LoadExtension("GL_ARB_texture_rectangle"); // Some OpenGL implementations such as Mesa or ATI // claim to support both GLSL and GL_ARB_texture_rectangle but // don't actually support sampler2DRectShadow in a GLSL code. // To test that, we compile the shader, if it fails, we don't use // deph peeling GLuint shader = vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::ShaderSource( shader, 1, const_cast(&vtkOpenGLRenderer_PeelingFS), 0); vtkgl::CompileShader(shader); GLint params; vtkgl::GetShaderiv(shader,vtkgl::COMPILE_STATUS, ¶ms); this->DepthPeelingIsSupported = params==GL_TRUE; vtkgl::DeleteShader(shader); if(!this->DepthPeelingIsSupported) { vtkDebugMacro("this OpenGL implementation does not support " "GL_ARB_texture_rectangle in GLSL code"); } } else { vtkDebugMacro(<<"depth peeling is not supported."); if(!supports_depth_texture) { vtkDebugMacro(<<"neither OpenGL 1.4 nor GL_ARB_depth_texture is supported"); } if(!supports_shadow) { vtkDebugMacro(<<"neither OpenGL 1.4 nor GL_ARB_shadow is supported"); } if(!supports_shadow_funcs) { vtkDebugMacro(<<"neither OpenGL 1.5 nor GL_EXT_shadow_funcs is supported"); } if(!supports_vertex_shader) { vtkDebugMacro(<<"neither OpenGL 2.0 nor GL_ARB_vertex_shader is supported"); } if(!supports_fragment_shader) { vtkDebugMacro(<<"neither OpenGL 2.0 nor GL_ARB_fragment_shader is supported"); } if(!supports_shader_objects) { vtkDebugMacro(<<"neither OpenGL 2.0 nor GL_ARB_shader_objects is supported"); } if(!supports_occlusion_query) { vtkDebugMacro(<<"neither OpenGL 1.5 nor GL_ARB_occlusion_query is supported"); } if(!supports_multitexture) { vtkDebugMacro(<<"neither OpenGL 1.3 nor GL_ARB_multitexture is supported"); } if(!supports_GL_ARB_texture_rectangle) { vtkDebugMacro(<<"GL_ARB_texture_rectangle is not supported"); } if(!supports_edge_clamp) { vtkDebugMacro(<<"neither OpenGL 1.2 nor GL_SGIS_texture_edge_clamp nor GL_EXT_texture_edge_clamp is not supported"); } if(!supportsAtLeast8AlphaBits) { vtkDebugMacro(<<"at least 8 alpha bits is not supported"); } if (!driver_support) { vtkDebugMacro(<<"buggy driver (Mesa < 6.5.3 or ATI)"); } } } } if(!this->UseDepthPeeling || !this->DepthPeelingIsSupported) { // just alpha blending this->LastRenderingUsedDepthPeeling=0; this->UpdateTranslucentPolygonalGeometry(); } else { // depth peeling. // get the viewport dimensions this->GetTiledSizeAndOrigin(&this->ViewportWidth,&this->ViewportHeight, &this->ViewportX,&this->ViewportY); // get z bits GLint depthBits; glGetIntegerv(GL_DEPTH_BITS,&depthBits); if(depthBits==16) { this->DepthFormat=vtkgl::DEPTH_COMPONENT16_ARB; } else { this->DepthFormat=vtkgl::DEPTH_COMPONENT24_ARB; } vtkgl::ActiveTexture(vtkgl::TEXTURE1); // check if the GPU supports a viewport sized texture in // the formats we will use. If not then we should fallback // to alpha blending. // This check is really an anachronism since modern GPU // typically support full screen sized textures in all the // common formats. GLint depthTexSupport = 1; GLint colorTexSupport = 1; if ( extensions->DriverIsATI() && !extensions->GetIgnoreDriverBugs("ATI proxy query bug.") ) { // The ATI Radeon HD drivers currently choke on the proxy // query, but depth peeling has been confirmed to work. For // those driver fall back on the weaker max texture size // check. GLint maxTexSize = 0; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); if ( (this->ViewportWidth > maxTexSize) || (this->ViewportHeight > maxTexSize) ) { depthTexSupport = 0; colorTexSupport = 0; } } else { // Not a buggy ATI driver, it's OK to make the proxy query. GLuint proxyQueryTex = 0; glGenTextures(1, &proxyQueryTex); glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB, proxyQueryTex); // support for depth buffer format glTexImage2D(vtkgl::PROXY_TEXTURE_RECTANGLE_ARB, 0, this->DepthFormat, this->ViewportWidth, this->ViewportHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,0); glGetTexLevelParameteriv(vtkgl::PROXY_TEXTURE_RECTANGLE_ARB, 0, GL_TEXTURE_WIDTH, &depthTexSupport); // support for color buffer glTexImage2D(vtkgl::PROXY_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, this->ViewportWidth, this->ViewportHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glGetTexLevelParameteriv(vtkgl::PROXY_TEXTURE_RECTANGLE_ARB, 0, GL_TEXTURE_WIDTH,&colorTexSupport); glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB, 0); glDeleteTextures(1, &proxyQueryTex); } if (!(depthTexSupport && colorTexSupport)) { // GPU does not support a view sized texture in this format. // Do alpha blending technique instead. vtkWarningMacro( << "The GPU supports " << this->ViewportWidth << "x" << this->ViewportHeight << " texture: depth buffer " << (depthTexSupport?"yes":"no") << ", color buffer " << (colorTexSupport?"yes":"no")); vtkgl::ActiveTexture(vtkgl::TEXTURE0); this->LastRenderingUsedDepthPeeling = 0; this->UpdateTranslucentPolygonalGeometry(); return; } // 1. Grab the RGBAZ of the opaque layer. GLuint opaqueLayerZ=0; GLuint opaqueLayerRgba=0; glGenTextures(1,&opaqueLayerZ); this->OpaqueLayerZ=opaqueLayerZ; glGenTextures(1,&opaqueLayerRgba); // opaque z format glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB,opaqueLayerZ); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB, vtkgl::TEXTURE_COMPARE_MODE, vtkgl::COMPARE_R_TO_TEXTURE); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB, vtkgl::TEXTURE_COMPARE_FUNC, GL_LESS); // Allocate memory glTexImage2D(vtkgl::TEXTURE_RECTANGLE_ARB,0,this->DepthFormat, this->ViewportWidth,this->ViewportHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0); // Grab the z-buffer glCopyTexSubImage2D(vtkgl::TEXTURE_RECTANGLE_ARB, 0, 0, 0, this->ViewportX, this->ViewportY,this->ViewportWidth, this->ViewportHeight); glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB,opaqueLayerRgba); // opaque rgba format glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Allocate memory glTexImage2D(vtkgl::TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, this->ViewportWidth,this->ViewportHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); // Have to be set before a call to UpdateTranslucentPolygonalGeometry() // because UpdateTranslucentPolygonalGeometry() will eventually call // vtkOpenGLActor::Render() that uses this flag. this->LastRenderingUsedDepthPeeling=1; // Grab the rgba-buffer glCopyTexSubImage2D(vtkgl::TEXTURE_RECTANGLE_ARB, 0, 0, 0, this->ViewportX, this->ViewportY,this->ViewportWidth, this->ViewportHeight); GLuint queryId; vtkgl::GenQueries(1,&queryId); int stop=0; int infiniteLoop=this->MaximumNumberOfPeels==0; unsigned int threshold=static_cast(this->ViewportWidth*this->ViewportHeight*OcclusionRatio); this->LayerList=new vtkOpenGLRendererLayerList; // save the default blend function. glPushAttrib(GL_COLOR_BUFFER_BIT); int multiSampleStatus=glIsEnabled(vtkgl::MULTISAMPLE); if(multiSampleStatus) { glDisable(vtkgl::MULTISAMPLE); } glDisable(GL_BLEND); GLuint nbPixels=0; GLuint previousNbPixels=0; int l=0; while(!stop) { vtkgl::BeginQuery(vtkgl::SAMPLES_PASSED,queryId); stop=!this->RenderPeel(l); vtkgl::EndQuery(vtkgl::SAMPLES_PASSED); // blocking call previousNbPixels=nbPixels; if(!stop || l>0) // stop && l==0 <=> no translucent geometry { vtkgl::GetQueryObjectuiv(queryId,vtkgl::QUERY_RESULT,&nbPixels); if(!stop) { stop=(nbPixels<=threshold) || (nbPixels==previousNbPixels); ++l; if(!stop && !infiniteLoop) { stop=l>=this->MaximumNumberOfPeels; } } } } if(multiSampleStatus) { glEnable(vtkgl::MULTISAMPLE); } // The two following lines are taken from vtkOpenGLProperty to // reset texturing state after rendering the props. glDisable(GL_TEXTURE_2D); glDisable (GL_ALPHA_TEST); glDepthFunc(GL_LEQUAL); vtkgl::DeleteQueries(1,&queryId); if(this->TransparentLayerZ!=0) { GLuint transparentLayerZ=static_cast(this->TransparentLayerZ); glDeleteTextures(1,&transparentLayerZ); this->TransparentLayerZ=0; } // Finally, draw sorted opacity glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, this->ViewportWidth, 0, this->ViewportHeight, -1, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glClearColor( static_cast(0),static_cast(0), static_cast(0),static_cast(0)); glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_DEPTH_TEST); vtkgl::ActiveTexture(vtkgl::TEXTURE0 ); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnable(vtkgl::TEXTURE_RECTANGLE_ARB); // actor in wireframe may have change that glPolygonMode(GL_FRONT, GL_FILL); glDisable(GL_BLEND); // First the opaque layer glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB,opaqueLayerRgba); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(0, 0); glTexCoord2f(this->ViewportWidth, 0); glVertex2f(this->ViewportWidth, 0); glTexCoord2f(this->ViewportWidth, this->ViewportHeight); glVertex2f(this->ViewportWidth, this->ViewportHeight); glTexCoord2f(0, this->ViewportHeight); glVertex2f(0, this->ViewportHeight); glEnd(); vtkgl::BlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); // the transparent layers std::list::reverse_iterator it=this->LayerList->List.rbegin(); std::list::reverse_iterator itEnd=this->LayerList->List.rend(); while(it!=itEnd) { glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB,(*it)); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(0, 0); glTexCoord2f(this->ViewportWidth, 0); glVertex2f(this->ViewportWidth, 0); glTexCoord2f(this->ViewportWidth, this->ViewportHeight); glVertex2f(this->ViewportWidth, this->ViewportHeight); glTexCoord2f(0, this->ViewportHeight); glVertex2f(0, this->ViewportHeight); glEnd(); ++it; } // Restore the default blend function for the next stage (overlay) glPopAttrib(); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glDisable(vtkgl::TEXTURE_RECTANGLE_ARB); glEnable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); // Destroy the shader if(this->ProgramShader!=0) { vtkgl::DeleteProgram(this->ProgramShader); this->ProgramShader=0; } // Destroy the layers size_t c=this->LayerList->List.size(); GLuint *ids=new GLuint[c]; std::list::const_iterator it2=this->LayerList->List.begin(); size_t layer=0; while(layer(c),ids); delete[] ids; delete this->LayerList; this->LayerList=0; glDeleteTextures(1,&opaqueLayerRgba); glDeleteTextures(1,&opaqueLayerZ); } vtkOpenGLCheckErrorMacro("failed after DeviceRenderTranslucentPolygonalGeometry"); } // ---------------------------------------------------------------------------- // Description: // Check the compilation status of some fragment shader source. void vtkOpenGLRenderer::CheckCompilation( unsigned int fragmentShader) { vtkOpenGLClearErrorMacro(); GLuint fs=static_cast(fragmentShader); GLint params; vtkgl::GetShaderiv(fs,vtkgl::COMPILE_STATUS,¶ms); if(params==GL_TRUE) { vtkDebugMacro(<<"shader source compiled successfully"); } else { vtkErrorMacro(<<"shader source compile error"); // include null terminator vtkgl::GetShaderiv(fs,vtkgl::INFO_LOG_LENGTH,¶ms); if(params>0) { char *buffer=new char[params]; vtkgl::GetShaderInfoLog(fs,params,0,buffer); vtkErrorMacro(<<"log: "<=0 int vtkOpenGLRenderer::RenderPeel(int layer) { assert("pre: positive_layer" && layer>=0); vtkOpenGLClearErrorMacro(); GLbitfield mask=GL_COLOR_BUFFER_BIT; if(layer>0) { mask=mask|GL_DEPTH_BUFFER_BIT; } glClearColor(0.0, 0.0, 0.0, 0.0); glClear(mask); vtkgl::ActiveTexture(vtkgl::TEXTURE2); glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB,this->OpaqueLayerZ); vtkgl::ActiveTexture(vtkgl::TEXTURE1 ); if(this->ProgramShader==0) { this->ProgramShader=vtkgl::CreateProgram(); GLuint shader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::ShaderSource(shader,1,const_cast(&vtkOpenGLRenderer_PeelingFS),0); vtkgl::CompileShader(shader); this->CheckCompilation(shader); vtkgl::AttachShader(this->ProgramShader,shader); vtkgl::LinkProgram(this->ProgramShader); GLint params; vtkgl::GetProgramiv(static_cast(this->ProgramShader),vtkgl::LINK_STATUS,¶ms); if(params==GL_TRUE) { vtkDebugMacro(<<"program linked successfully"); } else { vtkErrorMacro(<<"program link error"); // include null terminator vtkgl::GetProgramiv(static_cast(this->ProgramShader),vtkgl::INFO_LOG_LENGTH,¶ms); if(params>0) { #if 1 char *buffer=new char[params]; vtkgl::GetProgramInfoLog(static_cast(this->ProgramShader),params,0,buffer); vtkErrorMacro(<<"log: "<0) { glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB,this->TransparentLayerZ); vtkgl::UseProgram(this->ProgramShader); GLint uShadowTex=vtkgl::GetUniformLocation(this->ProgramShader,"shadowTex"); if(uShadowTex!=-1) { vtkgl::Uniform1i(uShadowTex,1); } else { vtkErrorMacro(<<"error: shadowTex is not a uniform."); } GLint uOpaqueShadowTex=vtkgl::GetUniformLocation(this->ProgramShader,"opaqueShadowTex"); if(uOpaqueShadowTex!=-1) { vtkgl::Uniform1i(uOpaqueShadowTex,2); } else { vtkErrorMacro(<<"error: opaqueShadowTex is not a uniform."); } GLint uOffsetX=vtkgl::GetUniformLocation(this->ProgramShader,"offsetX"); if(uOffsetX!=-1) { vtkgl::Uniform1f(uOffsetX,this->ViewportX); } else { vtkErrorMacro(<<"error: offsetX is not a uniform."); } GLint uOffsetY=vtkgl::GetUniformLocation(this->ProgramShader,"offsetY"); if(uOffsetY!=-1) { vtkgl::Uniform1f(uOffsetY,this->ViewportY); } else { vtkErrorMacro(<<"error: offsetY is not a uniform."); } } vtkgl::ActiveTexture(vtkgl::TEXTURE0 ); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); this->DepthPeelingHigherLayer=layer>0; int numberOfRenderedProps=this->UpdateTranslucentPolygonalGeometry(); if(layer>0) { this->DepthPeelingHigherLayer=0; vtkgl::UseProgram(0); } vtkgl::ActiveTexture(vtkgl::TEXTURE1 ); if(layer==0) { if(numberOfRenderedProps>0) { GLuint transparentLayerZ; glGenTextures(1,&transparentLayerZ); this->TransparentLayerZ=static_cast(transparentLayerZ); glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB,this->TransparentLayerZ); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB, vtkgl::TEXTURE_COMPARE_MODE, vtkgl::COMPARE_R_TO_TEXTURE); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB, vtkgl::TEXTURE_COMPARE_FUNC, GL_GREATER); // Allocate memory, note: verified GPU support for this // texture format above. glTexImage2D(vtkgl::TEXTURE_RECTANGLE_ARB,0,this->DepthFormat, this->ViewportWidth,this->ViewportHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0); } } else { glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB,this->TransparentLayerZ); } if((layer==0 && numberOfRenderedProps>0) || layer>0) { // Grab the z-buffer glCopyTexSubImage2D(vtkgl::TEXTURE_RECTANGLE_ARB, 0, 0, 0, this->ViewportX, this->ViewportY,this->ViewportWidth, this->ViewportHeight); // Grab the rgba buffer GLuint rgba; glGenTextures(1,&rgba); glBindTexture(vtkgl::TEXTURE_RECTANGLE_ARB,rgba); // rgba format glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(vtkgl::TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Allocate memory, note: verified GPU support for this // texture format above. glTexImage2D(vtkgl::TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, this->ViewportWidth,this->ViewportHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); // Grab the rgba-buffer glCopyTexSubImage2D(vtkgl::TEXTURE_RECTANGLE_ARB, 0, 0, 0, this->ViewportX, this->ViewportY,this->ViewportWidth, this->ViewportHeight); this->LayerList->List.push_back(rgba); vtkOpenGLCheckErrorMacro("failed after RenderPeel"); return 1; } else { vtkOpenGLCheckErrorMacro("failed after RenderPeel"); return 0; } } // ---------------------------------------------------------------------------- void vtkOpenGLRenderer::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "Number Of Lights Bound: " << this->NumberOfLightsBound << "\n"; os << indent << "PickBuffer " << this->PickInfo->PickBuffer << "\n"; os << indent << "PickedId" << this->PickInfo->PickedId<< "\n"; os << indent << "NumPicked" << this->PickInfo->NumPicked<< "\n"; os << indent << "PickedZ " << this->PickedZ << "\n"; } void vtkOpenGLRenderer::Clear(void) { vtkOpenGLClearErrorMacro(); GLbitfield clear_mask = 0; if (! this->Transparent()) { glClearColor(static_cast(this->Background[0]), static_cast(this->Background[1]), static_cast(this->Background[2]), static_cast(this->BackgroundAlpha)); clear_mask |= GL_COLOR_BUFFER_BIT; } if (!this->GetPreserveDepthBuffer()) { glClearDepth(static_cast(1.0)); clear_mask |= GL_DEPTH_BUFFER_BIT; } vtkDebugMacro(<< "glClear\n"); glClear(clear_mask); // If gradient background is turned on, draw it now. if (!this->Transparent() && (this->GradientBackground || this->TexturedBackground)) { double tile_viewport[4]; this->GetRenderWindow()->GetTileViewport(tile_viewport); glPushAttrib(GL_ENABLE_BIT | GL_TRANSFORM_BIT); glDisable(GL_ALPHA_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_1D); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glShadeModel(GL_SMOOTH); // color interpolation glMatrixMode(GL_MODELVIEW); glPushMatrix(); { glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); { glLoadIdentity(); glOrtho( tile_viewport[0], tile_viewport[2], tile_viewport[1], tile_viewport[3], -1.0, 1.0); //top vertices if(this->TexturedBackground && this->BackgroundTexture) { glEnable(GL_TEXTURE_2D); this->BackgroundTexture->Render(this); // NOTE: By default the mode is GL_MODULATE. Since the user // cannot set the mode, the default is set to replace. glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // NOTE: vtkTexture Render enables the alpha test // so that no buffer is affected if alpha of incoming fragment is // below the threshold. Here we have to enable it so that it won't // rejects the fragments of the quad as the alpha is set to 0 on it. glDisable(GL_ALPHA_TEST); } glBegin(GL_QUADS); glColor4d(this->Background[0],this->Background[1],this->Background[2], 0.0); glTexCoord2f(0.0, 0.0); glVertex2f(0.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex2f(1.0, 0); //bottom vertices glColor4d(this->Background2[0],this->Background2[1], this->Background2[2],0.0); glTexCoord2f(1.0, 1.0); glVertex2f(1.0, 1.0); glTexCoord2f(0.0, 1.0); glVertex2f(0.0, 1.0); glEnd(); } glMatrixMode(GL_PROJECTION); glPopMatrix(); } glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); } vtkOpenGLCheckErrorMacro("failed after Clear"); } void vtkOpenGLRenderer::StartPick(unsigned int pickFromSize) { vtkOpenGLClearErrorMacro(); int bufferSize = pickFromSize * 4; // Do not remove this MakeCurrent! Due to Start / End methods on // some objects which get executed during a pipeline update, // other windows might get rendered since the last time // a MakeCurrent was called. this->RenderWindow->MakeCurrent(); this->RenderWindow->IsPickingOn(); delete [] this->PickInfo->PickBuffer; this->PickInfo->PickBuffer = new GLuint[bufferSize]; glSelectBuffer(bufferSize, this->PickInfo->PickBuffer); // change to selection mode (void)glRenderMode(GL_SELECT); // initialize the pick names and add a 0 name, for no pick glInitNames(); glPushName(0); vtkOpenGLCheckErrorMacro("failed after StartPick"); } void vtkOpenGLRenderer::ReleaseGraphicsResources(vtkWindow *w) { if (w) { vtkRenderer::ReleaseGraphicsResources(w); if (this->Pass) { this->Pass->ReleaseGraphicsResources(w); } } } void vtkOpenGLRenderer::UpdatePickId() { glLoadName(this->CurrentPickId++); vtkOpenGLCheckErrorMacro("failed after UpdatePick"); } void vtkOpenGLRenderer::DevicePickRender() { // Do not remove this MakeCurrent! Due to Start / End methods on // some objects which get executed during a pipeline update, // other windows might get rendered since the last time // a MakeCurrent was called. this->RenderWindow->MakeCurrent(); vtkOpenGLClearErrorMacro(); // standard render method this->ClearLights(); this->UpdateCamera(); this->UpdateLightGeometry(); this->UpdateLights(); // set matrix mode for actors glMatrixMode(GL_MODELVIEW); this->PickGeometry(); // clean up the model view matrix set up by the camera glMatrixMode(GL_MODELVIEW); glPopMatrix(); vtkOpenGLCheckErrorMacro("failed after DevicePickRender"); } void vtkOpenGLRenderer::DonePick() { glFlush(); GLuint hits = glRenderMode(GL_RENDER); this->PickInfo->NumPicked = hits; unsigned int depth = static_cast(-1); GLuint* ptr = this->PickInfo->PickBuffer; this->PickInfo->PickedId = 0; for(unsigned int k =0; k < hits; k++) { int num_names = *ptr; int save = 0; ptr++; // move to first depth value if(*ptr <= depth) { depth = *ptr; save = 1; } ptr++; // move to next depth value if(*ptr <= depth) { depth = *ptr; save = 1; } // move to first name picked ptr++; if(save) { this->PickInfo->PickedId = *ptr; } // skip additional names ptr += num_names; } // If there was a pick, then get the Z value if(this->PickInfo->PickedId) { // convert from pick depth described as: // Returned depth values are mapped such that the largest unsigned // integer value corresponds to window coordinate depth 1.0, // and zero corresponds to window coordinate depth 0.0. this->PickedZ = depth/static_cast(VTK_UNSIGNED_INT_MAX); // Clamp to range [0,1] this->PickedZ = (this->PickedZ < 0.0) ? 0.0 : this->PickedZ; this->PickedZ = (this->PickedZ > 1.0) ? 1.0: this->PickedZ; } //Don't delete the list, keep it around in case caller wants all //of the hits. Delete it elsewhere when needed. //delete [] this->PickInfo->PickBuffer; //this->PickInfo->PickBuffer = 0; this->RenderWindow->IsPickingOff(); } double vtkOpenGLRenderer::GetPickedZ() { return this->PickedZ; } unsigned int vtkOpenGLRenderer::GetPickedId() { return static_cast(this->PickInfo->PickedId); } vtkOpenGLRenderer::~vtkOpenGLRenderer() { delete [] this->PickInfo->PickBuffer; this->PickInfo->PickBuffer = 0; delete this->PickInfo; if(this->ShaderProgram!=0) { this->ShaderProgram->Delete(); } if(this->Pass!=0) { this->Pass->UnRegister(this); } } unsigned int vtkOpenGLRenderer::GetNumPickedIds() { return static_cast(this->PickInfo->NumPicked); } int vtkOpenGLRenderer::GetPickedIds(unsigned int atMost, unsigned int *callerBuffer) { if (!this->PickInfo->PickBuffer) { return 0; } unsigned int max = (atMost < this->PickInfo->NumPicked) ? atMost : this->PickInfo->NumPicked; GLuint* iptr = this->PickInfo->PickBuffer; unsigned int *optr = callerBuffer; unsigned int k; for(k =0; k < max; k++) { int num_names = *iptr; iptr++; // move to first depth value iptr++; // move to next depth value iptr++; // move to first name picked *optr = static_cast(*iptr); optr++; // skip additional names iptr += num_names; } return k; }