diff --git a/source/MRCommonPlugins/ViewerButtons/MRViewerSettingsPlugin.cpp b/source/MRCommonPlugins/ViewerButtons/MRViewerSettingsPlugin.cpp index a06933fdeedf..f770e51e3e45 100644 --- a/source/MRCommonPlugins/ViewerButtons/MRViewerSettingsPlugin.cpp +++ b/source/MRCommonPlugins/ViewerButtons/MRViewerSettingsPlugin.cpp @@ -177,7 +177,12 @@ void ViewerSettingsPlugin::drawDialog( float menuScaling, ImGuiContext* ) ImGui::ColorEdit4( "Color", &shadowGl_->shadowColor.x, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_PickerHueWheel ); ImGui::DragFloat2( "Shift", &shadowGl_->shadowShift.x, 0.4f, -200.0f, 200.0f ); + ImGui::SetTooltipIfHovered( "X, Y shift in screen coordinates:\nX going right\nY going up", menuScaling ); ImGui::DragFloatValid( "Blur radius", &shadowGl_->blurRadius, 0.2f, 0, 200 ); + float quality = shadowGl_->getQuality(); + ImGui::DragFloatValid( "Quality", &quality, 0.0625f, 0.001f, 1.0f ); + ImGui::SetTooltipIfHovered( "Blur texture downscaling coefficient", menuScaling ); + shadowGl_->setQuality( quality ); } } ImGui::EndCustomStatePlugin(); diff --git a/source/MRViewer/MRGLStaticHolder.cpp b/source/MRViewer/MRGLStaticHolder.cpp index 7a31a4235f91..53b6efed17a9 100644 --- a/source/MRViewer/MRGLStaticHolder.cpp +++ b/source/MRViewer/MRGLStaticHolder.cpp @@ -1243,46 +1243,35 @@ void main(void) uniform vec4 color; uniform vec2 shift; uniform float blurRadius; + uniform bool convX; out vec4 outColor; // (out to render) fragment color - #define NUM_DIRECTIONS 12 - #define QUALITY 3 - - #define DIR_STEP 0.5235987756 - #define INV_QUALITY 0.33333334 - #define INV_NUM_SAMPLES 0.020833333 + const float gaussWeights[] = float[7] (0.161046, 0.148645, 0.116919, 0.078381, 0.044771, 0.021742, 0.009019); void main() { + gl_FragDepth = 0.9999; ivec2 texSize = textureSize( pixels, 0 ); - vec2 pos = gl_FragCoord.xy + shift; + vec2 pos = gl_FragCoord.xy; + if ( !convX ) + pos = pos - shift; pos = vec2( pos.x/float(texSize.x),pos.y/float(texSize.y) ); - - if ( texelFetch(pixels, ivec2( gl_FragCoord.xy ), 0 ).a == 1.0 ) - discard; - - if ( textureLod(pixels,pos,max(10.0,log2(blurRadius)+2.0)).a == 0.0 ) - discard; - - float avgValue = 0.0; - float maxRadiusSq = blurRadius*blurRadius; - - avgValue = texture(pixels, pos).a; - - for ( int r = 1; r <= QUALITY; r = r + 1 ) + vec2 posShift = vec2(0.0); + if ( convX ) + posShift = vec2(blurRadius /(6.0* float(texSize.x)),0.0); + else + posShift = vec2(0.0,blurRadius /(6.0* float(texSize.y))); + + float convSum = gaussWeights[0]*texture(pixels, pos).a; + for ( int i=1; i<=6; ++i ) { - float radius = float(r)*(blurRadius-0.5)*INV_QUALITY; - vec2 normRad = vec2(radius/float(texSize.x),radius/float(texSize.y)); - for ( int ang = 0; ang < NUM_DIRECTIONS; ang = ang + 1 ) - { - float realAng = float(ang)*DIR_STEP; - vec2 addPos = vec2(cos(realAng)*normRad.x, sin(realAng)*normRad.y); - avgValue = avgValue + texture(pixels, pos+addPos).a; - } + vec2 fullShift = float(i)*posShift; + convSum = convSum + gaussWeights[i]*texture(pixels, pos+fullShift).a; + convSum = convSum + gaussWeights[i]*texture(pixels, pos-fullShift).a; } - avgValue = avgValue * INV_NUM_SAMPLES; - outColor = vec4(color.rgb,avgValue*color.a); - gl_FragDepth = 0.9999; + outColor = vec4(color.rgb,convSum); + if ( !convX ) + outColor.a = outColor.a*color.a; if (outColor.a == 0.0) discard; } @@ -1294,11 +1283,16 @@ void main(void) MR_GLSL_VERSION_LINE R"( precision highp float; uniform sampler2D pixels; + uniform vec2 viewportSize; + uniform float depth; out vec4 outColor; // (out to render) fragment color void main() { - outColor = texelFetch(pixels, ivec2( gl_FragCoord.xy ), 0 ); + gl_FragDepth = depth; + vec2 pos = gl_FragCoord.xy; + pos = vec2( pos.x/float(viewportSize.x),pos.y/float(viewportSize.y) ); + outColor = texture(pixels, pos ); if (outColor.a == 0.0) discard; } diff --git a/source/MRViewer/MRShadowsGL.cpp b/source/MRViewer/MRShadowsGL.cpp index 71533605b074..88c91d6240f8 100644 --- a/source/MRViewer/MRShadowsGL.cpp +++ b/source/MRViewer/MRShadowsGL.cpp @@ -3,6 +3,7 @@ #include "MRGLMacro.h" #include "MRGladGlfw.h" #include "MRGLStaticHolder.h" +#include "MRCommandLoop.h" namespace MR { @@ -28,58 +29,246 @@ void ShadowsGL::enable( bool on ) { preDrawConnection_ = getViewerInstance().preDrawSignal.connect( MAKE_SLOT( &ShadowsGL::preDraw_ ), boost::signals2::at_back ); postDrawConnection_ = getViewerInstance().postDrawPreViewportSignal.connect( MAKE_SLOT( &ShadowsGL::postDraw_ ), boost::signals2::at_front ); + postResizeConnection_ = getViewerInstance().postResizeSignal.connect( MAKE_SLOT( &ShadowsGL::postResize_ ), boost::signals2::at_back ); + + glfwGetFramebufferSize( getViewerInstance().window, &sceneSize_.x, &sceneSize_.y ); + lowSize_ = Vector2i( Vector2f( sceneSize_ ) * quality_ ); + quadObject_.gen(); + sceneFramebuffer_.gen( sceneSize_, true ); + lowSizeFramebuffer_.gen( lowSize_, false ); + convolutionXFramebuffer_.gen( lowSize_, false ); } else { preDrawConnection_.disconnect(); postDrawConnection_.disconnect(); + postResizeConnection_.disconnect(); + + quadObject_.del(); + sceneFramebuffer_.del(); + convolutionXFramebuffer_.del(); + lowSizeFramebuffer_.del(); } } -void ShadowsGL::preDraw_() +void ShadowsGL::postResize_( int, int ) { glfwGetFramebufferSize( getViewerInstance().window, &sceneSize_.x, &sceneSize_.y ); if ( sceneSize_.x == 0 || sceneSize_.y == 0 ) return; + lowSize_ = Vector2i( Vector2f( sceneSize_ ) * quality_ ); + sceneFramebuffer_.del(); + convolutionXFramebuffer_.del(); + lowSizeFramebuffer_.del(); + + sceneFramebuffer_.gen( sceneSize_, true ); + lowSizeFramebuffer_.gen( lowSize_, false ); + convolutionXFramebuffer_.gen( lowSize_, false ); +} + +void ShadowsGL::preDraw_() +{ + sceneFramebuffer_.bind(); +} + +void ShadowsGL::postDraw_() +{ + if ( sceneSize_.x == 0 || sceneSize_.y == 0 ) + return; + +#ifndef __EMSCRIPTEN__ + GL_EXEC( glDisable( GL_MULTISAMPLE ) ); +#endif + sceneFramebuffer_.copyTexture(); + drawLowSize_(); // draw scene texture in low size for further convolution + convolveX_(); // draw shadow with x convolution to other texture (low res) + convolveY_(); // draw shadow with y convolution to other texture (low res) + drawTexture_( false, false ); // draw shadow in real size to main framebuffer + drawTexture_( true, false ); // draw scene in real size to main framebuffer + +#ifndef __EMSCRIPTEN__ + GL_EXEC( glEnable( GL_MULTISAMPLE ) ); +#endif +} + +void ShadowsGL::drawLowSize_() +{ + lowSizeFramebuffer_.bind(); + drawTexture_( true, true ); + lowSizeFramebuffer_.copyTexture(); +} + +void ShadowsGL::convolveX_() +{ + convolutionXFramebuffer_.bind(); + drawShadow_( true ); + convolutionXFramebuffer_.copyTexture(); +} + +void ShadowsGL::convolveY_() +{ + lowSizeFramebuffer_.bind(); // reuse this framebuffer for y conv + drawShadow_( false ); + lowSizeFramebuffer_.copyTexture(); +} + +void ShadowsGL::drawShadow_( bool convX ) +{ + GL_EXEC( glViewport( 0, 0, lowSize_.x, lowSize_.y ) ); + auto shader = GLStaticHolder::getShaderId( GLStaticHolder::ShadowOverlayQuad ); + GL_EXEC( glUseProgram( shader ) ); + + quadObject_.bind(); + GL_EXEC( glUniform4f( glGetUniformLocation( shader, "color" ), shadowColor.x, shadowColor.y, shadowColor.z, shadowColor.w ) ); + GL_EXEC( glUniform1f( glGetUniformLocation( shader, "blurRadius" ), blurRadius * quality_ ) ); + GL_EXEC( glUniform2f( glGetUniformLocation( shader, "shift" ), shadowShift.x * quality_, shadowShift.y * quality_) ); + GL_EXEC( glUniform1i( glGetUniformLocation( shader, "convX" ), convX ) ); + + GL_EXEC( glActiveTexture( GL_TEXTURE0 ) ); + if ( convX ) + { + // draw scene texture to texture with X convolution + GL_EXEC( glBindTexture( GL_TEXTURE_2D, lowSizeFramebuffer_.getTexture() ) ); + } + else + { + // draw X convolution texture to screen + GL_EXEC( glBindTexture( GL_TEXTURE_2D, convolutionXFramebuffer_.getTexture() ) ); + } + GL_EXEC( glUniform1i( glGetUniformLocation( shader, "pixels" ), 0 ) ); + getViewerInstance().incrementThisFrameGLPrimitivesCount( Viewer::GLPrimitivesType::TriangleArraySize, 2 ); + GL_EXEC( glDrawArrays( GL_TRIANGLES, 0, 6 ) ); +} + +void ShadowsGL::drawTexture_( bool scene, bool downsample ) +{ + const auto& size = downsample ? lowSize_ : sceneSize_; + GL_EXEC( glViewport( 0, 0, size.x, size.y ) ); + auto shader = GLStaticHolder::getShaderId( GLStaticHolder::SimpleOverlayQuad ); + GL_EXEC( glUseProgram( shader ) ); + + quadObject_.bind(); + GL_EXEC( glActiveTexture( GL_TEXTURE0 ) ); + if ( scene ) + { + GL_EXEC( glBindTexture( GL_TEXTURE_2D, sceneFramebuffer_.getTexture() ) ); + } + else + { + GL_EXEC( glBindTexture( GL_TEXTURE_2D, lowSizeFramebuffer_.getTexture() ) ); + } + if ( scene ) + { + GL_EXEC( glUniform1f( glGetUniformLocation( shader, "depth" ), 0.5f ) ); + } + else + { + GL_EXEC( glUniform1f( glGetUniformLocation( shader, "depth" ), 0.99f ) ); + } + GL_EXEC( glUniform2f( glGetUniformLocation( shader, "viewportSize" ), float( size.x ), float( size.y ) ) ); + GL_EXEC( glUniform1i( glGetUniformLocation( shader, "pixels" ), 0 ) ); + getViewerInstance().incrementThisFrameGLPrimitivesCount( Viewer::GLPrimitivesType::TriangleArraySize, 2 ); + GL_EXEC( glDrawArrays( GL_TRIANGLES, 0, 6 ) ); +} + +void ShadowsGL::setQuality( float quality ) +{ + if ( quality_ == quality ) + return; + if ( sceneSize_.x == 0 || sceneSize_.y == 0 ) + return; + + quality_ = quality; + if ( quality_ <= 0.0f ) + quality_ = 0.125f; + else if ( quality_ > 1.0f ) + quality_ = 1.0f; + if ( !enabled_ ) + return; - int samples; - GL_EXEC( glGetIntegerv( GL_SAMPLES, &samples ) ); + lowSize_ = Vector2i( Vector2f( sceneSize_ ) * quality_ ); + convolutionXFramebuffer_.del(); + lowSizeFramebuffer_.del(); + + lowSizeFramebuffer_.gen( lowSize_, false ); + convolutionXFramebuffer_.gen( lowSize_, false ); +} +void ShadowsGL::FramebufferData::gen( const Vector2i& size, bool multisample ) +{ // Create an initial multisampled framebuffer - GL_EXEC( glGenFramebuffers( 1, &sceneFramebuffer_ ) ); - GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, sceneFramebuffer_ ) ); + GL_EXEC( glGenFramebuffers( 1, &mainFramebuffer_ ) ); + GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, mainFramebuffer_ ) ); - // create a multisampled color renderbuffer - GL_EXEC( glGenRenderbuffers( 1, &sceneColorRenderbufferMultisampled_ ) ); - GL_EXEC( glBindRenderbuffer( GL_RENDERBUFFER, sceneColorRenderbufferMultisampled_ ) ); - GL_EXEC( glRenderbufferStorageMultisample( GL_RENDERBUFFER, samples, GL_RGBA8, sceneSize_.x, sceneSize_.y ) ); + // create a color renderbuffer + GL_EXEC( glGenRenderbuffers( 1, &colorRenderbuffer_ ) ); + GL_EXEC( glBindRenderbuffer( GL_RENDERBUFFER, colorRenderbuffer_ ) ); GL_EXEC( glBindRenderbuffer( GL_RENDERBUFFER, 0 ) ); - GL_EXEC( glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, sceneColorRenderbufferMultisampled_ ) ); - assert( glCheckFramebufferStatus( GL_FRAMEBUFFER ) == GL_FRAMEBUFFER_COMPLETE ); - // create a (also multisampled) renderbuffer object for depth attachments - GL_EXEC( glGenRenderbuffers( 1, &sceneDepthRenderbufferMultisampled_ ) ); - GL_EXEC( glBindRenderbuffer( GL_RENDERBUFFER, sceneDepthRenderbufferMultisampled_ ) ); - GL_EXEC( glRenderbufferStorageMultisample( GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT24, sceneSize_.x, sceneSize_.y ) ); + // create a renderbuffer object for depth attachments + GL_EXEC( glGenRenderbuffers( 1, &depthRenderbuffer_ ) ); + GL_EXEC( glBindRenderbuffer( GL_RENDERBUFFER, depthRenderbuffer_ ) ); GL_EXEC( glBindRenderbuffer( GL_RENDERBUFFER, 0 ) ); - GL_EXEC( glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, sceneDepthRenderbufferMultisampled_ ) ); - assert( glCheckFramebufferStatus( GL_FRAMEBUFFER ) == GL_FRAMEBUFFER_COMPLETE ); GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, 0 ) ); // configure second post-processing framebuffer - GL_EXEC( glGenFramebuffers( 1, &sceneCopyFramebuffer_ ) ); - GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, sceneCopyFramebuffer_ ) ); + GL_EXEC( glGenFramebuffers( 1, ©Framebuffer_ ) ); + GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, copyFramebuffer_ ) ); // create a color attachment texture - GL_EXEC( glGenTextures( 1, &sceneResTexture_ ) ); - GL_EXEC( glBindTexture( GL_TEXTURE_2D, sceneResTexture_ ) ); - GL_EXEC( glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, sceneSize_.x, sceneSize_.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ) ); - GL_EXEC( glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ) ); - GL_EXEC( glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) ); - GL_EXEC( glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sceneResTexture_, 0 ) ); + resTexture_.gen(); + GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, 0 ) ); + + resize_( size, multisample ); +} + +void ShadowsGL::FramebufferData::resize_( const Vector2i& size, bool multisample ) +{ + size_ = size; + int samples = 0; + if ( multisample ) + { + GL_EXEC( glGetIntegerv( GL_SAMPLES, &samples ) ); + } + + GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, mainFramebuffer_ ) ); + + GL_EXEC( glBindRenderbuffer( GL_RENDERBUFFER, colorRenderbuffer_ ) ); + if ( multisample ) + { + GL_EXEC( glRenderbufferStorageMultisample( GL_RENDERBUFFER, samples, GL_RGBA8, size.x, size.y ) ); + } + else + { + GL_EXEC( glRenderbufferStorage( GL_RENDERBUFFER, GL_RGBA8, size.x, size.y ) ); + } + GL_EXEC( glBindRenderbuffer( GL_RENDERBUFFER, 0 ) ); + GL_EXEC( glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer_ ) ); + assert( glCheckFramebufferStatus( GL_FRAMEBUFFER ) == GL_FRAMEBUFFER_COMPLETE ); + + GL_EXEC( glBindRenderbuffer( GL_RENDERBUFFER, depthRenderbuffer_ ) ); + if ( multisample ) + { + GL_EXEC( glRenderbufferStorageMultisample( GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT24, size.x, size.y ) ); + } + else + { + GL_EXEC( glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size.x, size.y ) ); + } + GL_EXEC( glBindRenderbuffer( GL_RENDERBUFFER, 0 ) ); + GL_EXEC( glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer_ ) ); + assert( glCheckFramebufferStatus( GL_FRAMEBUFFER ) == GL_FRAMEBUFFER_COMPLETE ); + + GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, copyFramebuffer_ ) ); + + resTexture_.loadData( { .resolution = size, .wrap = WrapType::Clamp,.filter = FilterType::Linear }, ( const char* ) nullptr ); + GL_EXEC( glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resTexture_.getId(), 0 ) ); assert( glCheckFramebufferStatus( GL_FRAMEBUFFER ) == GL_FRAMEBUFFER_COMPLETE ); GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, 0 ) ); +} - GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, sceneFramebuffer_ ) ); +void ShadowsGL::FramebufferData::bind() +{ + GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, mainFramebuffer_ ) ); // Clear the buffer float cClearValue[4] = { 0.0f,0.0f,0.0f,0.0f }; @@ -87,20 +276,28 @@ void ShadowsGL::preDraw_() GL_EXEC( glClear( GL_DEPTH_BUFFER_BIT ) ); } -void ShadowsGL::postDraw_() +void ShadowsGL::FramebufferData::copyTexture() { - if ( sceneSize_.x == 0 || sceneSize_.y == 0 ) - return; - - GL_EXEC( glBindFramebuffer( GL_READ_FRAMEBUFFER, sceneFramebuffer_ ) ); - GL_EXEC( glBindFramebuffer( GL_DRAW_FRAMEBUFFER, sceneCopyFramebuffer_ ) ); - GL_EXEC( glBlitFramebuffer( 0, 0, sceneSize_.x, sceneSize_.y, 0, 0, sceneSize_.x, sceneSize_.y, GL_COLOR_BUFFER_BIT, GL_NEAREST ) ); + GL_EXEC( glBindFramebuffer( GL_READ_FRAMEBUFFER, mainFramebuffer_ ) ); + GL_EXEC( glBindFramebuffer( GL_DRAW_FRAMEBUFFER, copyFramebuffer_ ) ); + GL_EXEC( glBlitFramebuffer( 0, 0, size_.x, size_.y, 0, 0, size_.x, size_.y, GL_COLOR_BUFFER_BIT, GL_NEAREST ) ); GL_EXEC( glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 ) ); GL_EXEC( glBindFramebuffer( GL_READ_FRAMEBUFFER, 0 ) ); GL_EXEC( glBindFramebuffer( GL_FRAMEBUFFER, 0 ) ); - - // Draw shadows and main texture +} + +void ShadowsGL::FramebufferData::del() +{ + resTexture_.del(); + GL_EXEC( glDeleteFramebuffers( 1, &mainFramebuffer_ ) ); + GL_EXEC( glDeleteFramebuffers( 1, ©Framebuffer_ ) ); + GL_EXEC( glDeleteRenderbuffers( 1, &depthRenderbuffer_ ) ); + GL_EXEC( glDeleteRenderbuffers( 1, &colorRenderbuffer_ ) ); +} + +void ShadowsGL::QuadTextureVertexObject::gen() +{ constexpr GLfloat quad[18] = { -1.0f, -1.0f, 0.99f, @@ -110,77 +307,26 @@ void ShadowsGL::postDraw_() 1.0f, -1.0f, 0.99f, 1.0f, 1.0f, 0.99f }; -#ifndef __EMSCRIPTEN__ - GL_EXEC( glDisable( GL_MULTISAMPLE ) ); -#endif - GL_EXEC( glViewport( 0, 0, sceneSize_.x, sceneSize_.y ) ); - - GLuint quadVao = 0; - GLuint quadVbo = 0; - GL_EXEC( glGenVertexArrays( 1, &quadVao ) ); - GL_EXEC( glGenBuffers( 1, &quadVbo ) ); + GL_EXEC( glGenVertexArrays( 1, &vao_ ) ); + GL_EXEC( glGenBuffers( 1, &vbo_ ) ); // draw shadows - GL_EXEC( glBindVertexArray( quadVao ) ); - - auto shader = GLStaticHolder::getShaderId( GLStaticHolder::ShadowOverlayQuad ); - GL_EXEC( glUseProgram( shader ) ); - - GL_EXEC( glBindBuffer( GL_ARRAY_BUFFER, quadVbo ) ); - GL_EXEC( glBufferData( GL_ARRAY_BUFFER, sizeof( GLfloat ) * 18, quad, GL_STATIC_DRAW ) ); - - GL_EXEC( glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, 0 ) ); - GL_EXEC( glEnableVertexAttribArray( 0 ) ); - - GL_EXEC( glUniform4f( glGetUniformLocation( shader, "color" ), shadowColor.x, shadowColor.y, shadowColor.z, shadowColor.w ) ); - GL_EXEC( glUniform1f( glGetUniformLocation( shader, "blurRadius" ), blurRadius ) ); - GL_EXEC( glUniform2f( glGetUniformLocation( shader, "shift" ), shadowShift.x, shadowShift.y ) ); - - GL_EXEC( glActiveTexture( GL_TEXTURE0 ) ); - GL_EXEC( glBindTexture( GL_TEXTURE_2D, sceneResTexture_ ) ); - GL_EXEC( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) ); - GL_EXEC( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) ); - GL_EXEC( glGenerateMipmap(GL_TEXTURE_2D) ); - GL_EXEC( glUniform1i( glGetUniformLocation( shader, "pixels" ), 0 ) ); - - GL_EXEC( glBindVertexArray( quadVao ) ); - - getViewerInstance().incrementThisFrameGLPrimitivesCount( Viewer::GLPrimitivesType::TriangleArraySize, 2 ); - GL_EXEC( glDrawArrays( GL_TRIANGLES, 0, 6 ) ); - - // draw main texture - GL_EXEC( glBindVertexArray( quadVao ) ); - - shader = GLStaticHolder::getShaderId( GLStaticHolder::SimpleOverlayQuad ); - GL_EXEC( glUseProgram( shader ) ); - - GL_EXEC( glBindBuffer( GL_ARRAY_BUFFER, quadVbo ) ); + GL_EXEC( glBindBuffer( GL_ARRAY_BUFFER, vbo_ ) ); GL_EXEC( glBufferData( GL_ARRAY_BUFFER, sizeof( GLfloat ) * 18, quad, GL_STATIC_DRAW ) ); +} +void ShadowsGL::QuadTextureVertexObject::bind() +{ + GL_EXEC( glBindVertexArray( vao_ ) ); + GL_EXEC( glBindBuffer( GL_ARRAY_BUFFER, vbo_ ) ); GL_EXEC( glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, 0 ) ); GL_EXEC( glEnableVertexAttribArray( 0 ) ); +} - GL_EXEC( glActiveTexture( GL_TEXTURE0 ) ); - GL_EXEC( glBindTexture( GL_TEXTURE_2D, sceneResTexture_ ) ); - GL_EXEC( glUniform1i( glGetUniformLocation( shader, "pixels" ), 0 ) ); - - GL_EXEC( glBindVertexArray( quadVao ) ); - - getViewerInstance().incrementThisFrameGLPrimitivesCount( Viewer::GLPrimitivesType::TriangleArraySize, 2 ); - GL_EXEC( glDrawArrays( GL_TRIANGLES, 0, 6 ) ); -#ifndef __EMSCRIPTEN__ - GL_EXEC( glEnable( GL_MULTISAMPLE ) ); -#endif - - GL_EXEC( glDeleteVertexArrays( 1, &quadVao ) ); - GL_EXEC( glDeleteBuffers( 1, &quadVbo ) ); - - // Clean up - GL_EXEC( glDeleteTextures( 1, &sceneResTexture_ ) ); - GL_EXEC( glDeleteFramebuffers( 1, &sceneFramebuffer_ ) ); - GL_EXEC( glDeleteFramebuffers( 1, &sceneCopyFramebuffer_ ) ); - GL_EXEC( glDeleteRenderbuffers( 1, &sceneDepthRenderbufferMultisampled_ ) ); - GL_EXEC( glDeleteRenderbuffers( 1, &sceneColorRenderbufferMultisampled_ ) ); +void ShadowsGL::QuadTextureVertexObject::del() +{ + GL_EXEC( glDeleteVertexArrays( 1, &vao_ ) ); + GL_EXEC( glDeleteBuffers( 1, &vbo_ ) ); } } \ No newline at end of file diff --git a/source/MRViewer/MRShadowsGL.h b/source/MRViewer/MRShadowsGL.h index 5288cde81a09..a0c751f2781e 100644 --- a/source/MRViewer/MRShadowsGL.h +++ b/source/MRViewer/MRShadowsGL.h @@ -3,6 +3,7 @@ #include "MRMesh/MRVector2.h" #include "MRMesh/MRVector4.h" #include "MRMesh/MRColor.h" +#include "MRRenderGLHelpers.h" #include namespace MR @@ -24,21 +25,64 @@ class MRVIEWER_CLASS ShadowsGL // shift in screen space Vector2f shadowShift = Vector2f( 0.0, 0.0 ); Vector4f shadowColor = Vector4f( Color::yellow() ); - float blurRadius{ 3.0f }; + float blurRadius{ 40.0f }; + // value that describes blur quality, blur texture downscaling coefficient + // (0,1] 1 - is maximum quality, but it can affect performance on embedded systems + // 0.25 - recommended value + float getQuality() const { return quality_; } + MRVIEWER_API void setQuality( float quality ); private: + float quality_{ 0.25f }; void preDraw_(); void postDraw_(); + void postResize_( int x, int y ); + + void drawLowSize_(); + void convolveX_(); + void convolveY_(); + void drawShadow_( bool convX ); + void drawTexture_( bool scene, bool downsample ); boost::signals2::connection preDrawConnection_; boost::signals2::connection postDrawConnection_; + boost::signals2::connection postResizeConnection_; Vector2i sceneSize_; + Vector2i lowSize_; - unsigned int sceneFramebuffer_{ 0 }; - unsigned int sceneColorRenderbufferMultisampled_{ 0 }; - unsigned int sceneDepthRenderbufferMultisampled_{ 0 }; - unsigned int sceneCopyFramebuffer_{ 0 }; - unsigned int sceneResTexture_{ 0 }; + class FramebufferData + { + public: + void gen( const Vector2i& size, bool multisample ); + void bind(); + void copyTexture(); + void del(); + unsigned getTexture() const { return resTexture_.getId(); } + private: + void resize_( const Vector2i& size, bool multisample ); + + unsigned mainFramebuffer_{ 0 }; + unsigned colorRenderbuffer_{ 0 }; + unsigned depthRenderbuffer_{ 0 }; + unsigned copyFramebuffer_{ 0 }; + GlTexture2 resTexture_; + Vector2i size_; + }; + + class QuadTextureVertexObject + { + public: + void gen(); + void bind(); + void del(); + private: + unsigned vao_; + unsigned vbo_; + } quadObject_; + + FramebufferData sceneFramebuffer_; + FramebufferData lowSizeFramebuffer_; + FramebufferData convolutionXFramebuffer_; bool enabled_{ false }; };