2015-11-06 112 views
4

我正在尋找在我的WPF應用程序中具有半透明窗體/彈出窗口,並且我完全相同的是同一種類型的航空主題暗玻璃模糊實現在Windows 10如下所示:對所選容器背後的所有內容應用模糊

Aero blur effect example

因此到目前爲止,我只找到任何資源網上講解如何應用內的容器中是完全相反的這種模糊效果的一切我之後,或者將相同類型的模糊廣泛應用於表單/彈出框後面的所有內容。

我知道SetWindowCompositionAttribute 的(如下圖所示:Native Aero Blur without Glass Effect on Borderless WPF Window這裏進一步解釋說:http://withinrafael.com/adding-the-aero-glass-blur-to-your-windows-10-apps/

但這些只能解釋增加的影響到應用程序的整個窗口,這是不正是我什麼後。

我想將效果應用到我的應用程序中的選定元素(比如說一個邊框);

enter image description here

...喜歡這樣。

我該如何去做這樣的事情?

+0

我的想法是採取「屏幕截圖」,然後模糊它,並把它作爲控制的背景。我看到很多開發人員都渴望擁有像Background =「Blur」這樣的功能,但是我沒有通過任何解決方案來解決這個問題。 –

+0

@MichalKozak當然這是錯的。 –

+2

http://journalsill.hjtcentral.com/Home/tabid/428/EntryId/403/Glass-Behavior-for-WPF.aspx –

回答

2

我希望回答您的問題還爲時不晚。在我看來,獲得結果的解決方案由ShaderEffect類表示。

我的方法有一些限制,但我想可以改進它來解決它們。

首先,讓我們看到的結果是:

Blur Sample

我的想法是創建一個自定義ShaderEffect,我叫BlurRectEffect。爲了理解這個課程,你可以閱讀this很好的教程。

public class RectBlurEffect : ShaderEffect 
{ 
    private static PixelShader pixelShader = new PixelShader(); 
    private static PropertyInfo propertyInfo; 

    public static readonly DependencyProperty InputProperty = 
     ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(RectBlurEffect), 0); 

    public static readonly DependencyProperty UpLeftCornerProperty = 
     DependencyProperty.Register("UpLeftCorner", typeof(Point), typeof(RectBlurEffect), 
      new UIPropertyMetadata(new Point(0, 0), PixelShaderConstantCallback(0))); 

    public static readonly DependencyProperty LowRightCornerProperty = 
     DependencyProperty.Register("LowRightCorner", typeof(Point), typeof(RectBlurEffect), 
      new UIPropertyMetadata(new Point(1, 1), PixelShaderConstantCallback(1))); 

    public static readonly DependencyProperty FrameworkElementProperty = 
     DependencyProperty.Register("FrameworkElement", typeof(FrameworkElement), typeof(RectBlurEffect), 
     new PropertyMetadata(null, OnFrameworkElementPropertyChanged)); 

    static RectBlurEffect() 
    { 
     pixelShader.UriSource = Global.MakePackUri("RectBlurEffect.ps"); 
     propertyInfo = typeof(RectBlurEffect).GetProperty("InheritanceContext", 
      BindingFlags.Instance | BindingFlags.NonPublic); 
    }   

    public RectBlurEffect() 
    { 
     PixelShader = pixelShader; 
     UpdateShaderValue(InputProperty); 
     UpdateShaderValue(UpLeftCornerProperty); 
     UpdateShaderValue(LowRightCornerProperty); 
    } 

    public Brush Input 
    { 
     get { return (Brush)GetValue(InputProperty); } 
     set { SetValue(InputProperty, value); } 
    }   

    public Point UpLeftCorner 
    { 
     get { return (Point)GetValue(UpLeftCornerProperty); } 
     set { SetValue(UpLeftCornerProperty, value); } 
    }   

    public Point LowRightCorner 
    { 
     get { return (Point)GetValue(LowRightCornerProperty); } 
     set { SetValue(LowRightCornerProperty, value); } 
    } 

    public FrameworkElement FrameworkElement 
    { 
     get { return (FrameworkElement)GetValue(FrameworkElementProperty); } 
     set { SetValue(FrameworkElementProperty, value); } 
    } 

    private FrameworkElement GetInheritanceContext() 
    { 
     return propertyInfo.GetValue(this, null) as FrameworkElement; 
    } 

    private void UpdateEffect(object sender, EventArgs args) 
    { 
     Rect underRectangle; 
     Rect overRectangle; 
     Rect intersect; 

     FrameworkElement under = GetInheritanceContext(); 
     FrameworkElement over = this.FrameworkElement; 

     Point origin = under.PointToScreen(new Point(0, 0)); 
     underRectangle = new Rect(origin.X, origin.Y, under.ActualWidth, under.ActualHeight); 

     origin = over.PointToScreen(new Point(0, 0)); 
     overRectangle = new Rect(origin.X, origin.Y, over.ActualWidth, over.ActualHeight); 

     intersect = Rect.Intersect(overRectangle, underRectangle); 

     if (intersect.IsEmpty) 
     { 
      UpLeftCorner = new Point(0, 0); 
      LowRightCorner = new Point(0, 0); 
     } 
     else 
     { 
      origin = new Point(intersect.X, intersect.Y); 
      origin = under.PointFromScreen(origin); 

      UpLeftCorner = new Point(origin.X/under.ActualWidth, 
       origin.Y/under.ActualHeight); 
      LowRightCorner = new Point(UpLeftCorner.X + (intersect.Width/under.ActualWidth), 
       UpLeftCorner.Y + (intersect.Height/under.ActualHeight)); 
     } 

    } 

    private static void OnFrameworkElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs args) 
    { 
     RectBlurEffect rectBlurEffect = (RectBlurEffect)d; 

     FrameworkElement frameworkElement = args.OldValue as FrameworkElement; 

     if (frameworkElement != null) 
     { 
      frameworkElement.LayoutUpdated -= rectBlurEffect.UpdateEffect; 
     } 

     frameworkElement = args.NewValue as FrameworkElement; 

     if (frameworkElement != null) 
     { 
      frameworkElement.LayoutUpdated += rectBlurEffect.UpdateEffect; 
     } 
    } 
} 

這個類計算受影響的控件和疊加的控件之間的交集。然後它將這些信息發送到所謂的「像素着色器」文件(.fx文件,該文件將被編譯成.ps文件)。

現在我們需要創建RectBlueEffect.fx文件。它使用HLSL - (i.e. High Level Shading Language)。這裏它的內容:

sampler2D rectBlurEffect : register(S0); 
float2 upperLeftCorner : register(C0); 
float2 lowerRightCorner : register(C1); 

float Angle : register(C2); 
float BlurAmount : register(C3); 

float PI = 3.14159265358979323846; 
float EPSILON = 0.0001; 

float ComputeGaussian(float n) 
{ 
    float theta = 2.0f + EPSILON; //float.Epsilon; 

    return theta = (float)((1.0/sqrt(2 * PI * theta)) * 
     exp(-(n * n)/(2 * theta * theta))); 
} 

float4 gaussianblur(float2 texCoord: TEXCOORD0) : COLOR 
{ 

    float SampleWeights[7]; 
    float2 SampleOffsets[15]; 

    // The first sample always has a zero offset. 
    float2 initer = { 0.0f, 0.0f }; 
    SampleWeights[0] = ComputeGaussian(0); 
    SampleOffsets[0] = initer; 

    // Maintain a sum of all the weighting values. 
    float totalWeights = SampleWeights[0]; 

    // Add pairs of additional sample taps, positioned 
    // along a line in both directions from the center. 
    for (int i = 0; i < 7/2; i++) 
    { 
     // Store weights for the positive and negative taps. 
     float weight = ComputeGaussian(i + 1); 

     SampleWeights[i * 2 + 1] = weight; 
     SampleWeights[i * 2 + 2] = weight; 

     totalWeights += weight * 2; 


     float sampleOffset = i * 2 + 1.5f; 

     float2 delta = { (1.0f/512), 0 }; 
     delta = delta * sampleOffset; 

     // Store texture coordinate offsets for the positive and negative taps. 
     SampleOffsets[i * 2 + 1] = delta; 
     SampleOffsets[i * 2 + 2] = -delta; 
    } 

    // Normalize the list of sample weightings, so they will always sum to one. 
    for (int j = 0; j < 7; j++) 
    { 
     SampleWeights[j] /= totalWeights; 
    } 

    float4 color = 0.0f; 

    for (int k = 0; k < 7; k++) 
    { 
     color += tex2D(rectBlurEffect, 
      texCoord + SampleOffsets[k]) * SampleWeights[k]; 
    } 

    return color; 
} 

float4 directionalBlur(float2 uv : TEXCOORD) : COLOR 
{ 
    float4 c = 0; 
    float rad = Angle * 0.0174533f; 
    float xOffset = cos(rad); 
    float yOffset = sin(rad); 

    for (int i = 0; i < 12; i++) 
    { 
     uv.x = uv.x - BlurAmount * xOffset; 
     uv.y = uv.y - BlurAmount * yOffset; 
     c += tex2D(rectBlurEffect, uv); 
    } 
    c /= 12; 

    return c; 
} 

float4 main(float2 uv : TEXCOORD) : COLOR 
{ 
    if (uv.x < upperLeftCorner.x || uv.y < upperLeftCorner.y || uv.x > lowerRightCorner.x || uv.y > lowerRightCorner.y) 
    { 
     return tex2D(rectBlurEffect, uv); 
    } 

    return gaussianblur(uv); 
} 

正如你可以看到,如果像素是矩形(這兩個控制區域之間的交點)以外不施加影響。否則main方法使用效果(即高斯模糊)。

你可以找到here這個方法的實現,我叫gaussianblur。只要看看最後的答案。

現在我們必須編譯RectBlueEffect.fx文件。您可以在上一個鏈接(教程)或here中找到一些說明。

的XAML是我的解決方案的最後一部分:

<Grid> 
    <StackPanel Orientation="Vertical" VerticalAlignment="Center"> 
     <Border Background="Brown" BorderThickness="0" Name="tb"> 
      <TextBlock Text="Hello World!" Margin="4" Padding="10" 
         FontSize="30" FontWeight="Bold" Foreground="Yellow" 
         VerticalAlignment="Center" 
         HorizontalAlignment="Stretch" 
         TextAlignment="Center" /> 
      <Border.Effect> 
       <local:RectBlurEffect FrameworkElement="{Binding ElementName=Border, Mode=OneTime}" /> 
      </Border.Effect> 
     </Border> 

     <TextBlock Background="Khaki" Text="I should be partially blurred!" Margin="4" Padding="10" 
      Foreground="DarkGreen" FontSize="30" TextWrapping="Wrap" FontFamily="Cambria" 
      VerticalAlignment="Center" 
      HorizontalAlignment="Stretch" 
      TextAlignment="Center"> 
      <TextBlock.Effect> 
       <local:RectBlurEffect FrameworkElement="{Binding ElementName=Border, Mode=OneTime}" /> 
      </TextBlock.Effect> 
     </TextBlock> 
    </StackPanel> 

    <Border Name="Border" Width="200" Height="260" BorderThickness="0" 
      Background="Black" Opacity=".6" Panel.ZIndex="20"> 

     <TextBlock Text="Pretend I'm a border dumped over a grid" TextWrapping="Wrap" 
        HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="16" 
        Foreground="AntiqueWhite" Background="Transparent" 
        Margin="8" /> 

    </Border> 
</Grid> 

如你想我的解決方案允許重疊只是矩形(或正方形),但它不爲橢圓形,圓形或不規則多邊形工作。當然,一切都取決於您在.fx文件中編寫的HLSL代碼。

+0

您能否提供我可以用來編譯.ps文件的完整.fx文件。目前我正在使用的是拋出「無效的用戶指定像素着色器。註冊PixelShader.InvalidPixelShaderEncountered事件處理程序,以避免此異常被引發」。謝謝你的時間。 – MoonKnight

+1

@MoonKnight,我剛剛添加了我的_.fx_文件的整個代碼。我希望我能對你更有用 –

+0

非常感謝你的時間。 – MoonKnight