【发布时间】:2021-10-02 06:04:54
【问题描述】:
我正在使用 kivy & kivymd,我正在尝试以最佳方式将高斯模糊应用于作为背景图像的 FitImage。
当然,我尝试了 EffectWidget,但是,考虑到我为它设置动画并且我希望用户能够调整窗口大小和根据文档,每次小部件发生更改时,效果小部件都会重新创建 fbo。
我使用的代码:
from kivy.lang import Builder
from kivymd.app import MDApp
class MainApp(MDApp):
def __init__(self, **kwargs):
super(MainApp, self).__init__(**kwargs)
self.kv = Builder.load_string('''
#:kivy 2.0.0
#:import ew kivy.uix.effectwidget
EffectWidget:
effects: ew.HorizontalBlurEffect(size=12.0), ew.VerticalBlurEffect(size=12.0)
FitImage:
source: "images/song_img.jpg"
# the rest of my code...
''')
def build(self):
return self.kv
if __name__ == '__main__':
MainApp().run()
所以我认为实现我想要的唯一合适的方法是更改 glsl 代码。
ew.HorizontalBlurEffect的glsl代码:
effect_blur_h = '''
vec4 effect(vec4 color, sampler2D texture, vec2 tex_coords, vec2 coords)
{{
float dt = ({} / 4.0) * 1.0 / resolution.x;
vec4 sum = vec4(0.0);
sum += texture2D(texture, vec2(tex_coords.x - 4.0*dt, tex_coords.y))
* 0.05;
sum += texture2D(texture, vec2(tex_coords.x - 3.0*dt, tex_coords.y))
* 0.09;
sum += texture2D(texture, vec2(tex_coords.x - 2.0*dt, tex_coords.y))
* 0.12;
sum += texture2D(texture, vec2(tex_coords.x - dt, tex_coords.y))
* 0.15;
sum += texture2D(texture, vec2(tex_coords.x, tex_coords.y))
* 0.16;
sum += texture2D(texture, vec2(tex_coords.x + dt, tex_coords.y))
* 0.15;
sum += texture2D(texture, vec2(tex_coords.x + 2.0*dt, tex_coords.y))
* 0.12;
sum += texture2D(texture, vec2(tex_coords.x + 3.0*dt, tex_coords.y))
* 0.09;
sum += texture2D(texture, vec2(tex_coords.x + 4.0*dt, tex_coords.y))
* 0.05;
return vec4(sum.xyz, color.w);
}}
'''
根据文档,AdvancedEffectBase 可以帮助解决这些问题,但是,问题是我不知道如何更改这些 glsl 代码以实现我想要的。
我尝试使用其他人的 glsl 代码来应用像这样的高斯模糊效果:
Shadertoy's code for gaussian blur effect
还有其他人……
我应该如何实现我想要的?
更新:@Tshirtman 的答案似乎是迄今为止最好的答案,但是,我个人遇到了问题。
我在单独的屏幕中使用模糊图像,图像似乎没有跟随屏幕管理器的过渡动画,而是在其他小部件慢慢到位时显示出来。这是可以修复的吗?另外,有没有办法提高模糊的分辨率?有点像低分辨率。
我的代码:
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.graphics import RenderContext
from kivymd.utils.fitimage import FitImage
from kivymd.app import MDApp
class BlurredBackgroundImage(FitImage):
def __init__(self, **kwargs):
fs = '''
$HEADER$
uniform vec2 resolution;
void main(void) {
int radius = 4;
vec2 d = float(radius) / resolution;
for (int dx = -radius; dx < radius; dx++)
for (int dy = -radius; dy < radius; dy++)
gl_FragColor += texture2D(texture0, tex_coord0 + vec2(float(dx), float(dy)) * d);
gl_FragColor /= float( 4 * radius * radius);
}
'''
self.canvas = RenderContext()
self.canvas.shader.fs = fs
super(BlurredBackgroundImage, self).__init__(**kwargs)
def on_size(self, *args):
self.canvas['projection_mat'] = Window.render_context['projection_mat']
self.canvas['modelview_mat'] = Window.render_context['modelview_mat']
self.canvas['resolution'] = list(map(float, self.size))
print("size changed")
# tried updating the shader whenever the position changes but still no improvements
'''def on_pos(self, *args):
self.canvas['projection_mat'] = Window.render_context['projection_mat']
self.canvas['modelview_mat'] = Window.render_context['modelview_mat']
self.canvas['resolution'] = list(map(float, self.size))
print("pos changed")'''
class MainApp(MDApp):
def __init__(self, **kwargs):
super(MainApp, self).__init__(**kwargs)
self.kv = Builder.load_string('''
ScreenManager:
Screen:
name: "main-menu"
Button:
text: "Go to next Page!"
on_release:
root.transition.direction = "down"
root.current = "song-view"
Screen:
name: "song-view"
RelativeLayout:
BlurredBackgroundImage:
source: "images/song_img.jpg"
Button:
text: "Return to main menu!"
size_hint: .25, .25
pos_hint: {"center_x": .5, "center_y": .5}
on_release:
root.transition.direction = "up"
root.current = "main-menu"
''')
def build(self):
return self.kv
if __name__ == '__main__':
MainApp().run()
【问题讨论】:
标签: python python-3.x kivy kivymd gaussianblur