2012-07-25 68 views
5

我遇到了WPF中像素着色器的奇怪行爲。應用了着色器效果後的模糊圖片

這個問題是100%可重複的,所以我寫了一個小的演示程序。你可以下載源代碼here

所有罪惡的根源是微小的類名爲MyFrameworkElement:

internal sealed class MyFrameworkElement : FrameworkElement 
{ 
    public double EndX 
    { 
     get 
     { 
      return (double)this.GetValue(MyFrameworkElement.EndXProperty); 
     } 
     set 
     { 
      this.SetValue(MyFrameworkElement.EndXProperty, value); 
     } 
    } 

    public static readonly DependencyProperty EndXProperty = 
     DependencyProperty.Register("EndX", 
      typeof(double), 
      typeof(MyFrameworkElement), 
      new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender)); 

    protected override void OnRender(DrawingContext dc) 
    { 
     dc.DrawLine(new Pen(Brushes.Red, 2), new Point(0, 0), new Point(this.EndX, 100)); 
     dc.DrawLine(new Pen(Brushes.Green, 3), new Point(10, 300), new Point(200, 10)); 
    } 
} 

正如你可以看到這個框架元素呈現2線:低線有永久的座標,但上線取決於EndX依賴屬性。

所以這個框架元素是像素着色器效果的目標。爲了簡單起見,我使用找到的灰度着色器效果here。所以我將GrayscaleEffect應用於MyFrameworkElement。你可以看到結果,它看起來不錯。

直到我增加EndX性能急劇下降。

小線是模糊的大線是好的!

但是,如果我刪除灰度效果,所有行將看起來應該如此。

任何人能解釋這是什麼不清楚的原因是什麼? 甚至更​​好,我該如何解決這個問題?

+0

似乎是一個BitmapEffect對於巨大值的限制。 – LPL 2012-07-25 13:35:58

+0

@LPL如果將第二條(較低)行綁定到EndX屬性,然後將EndX設置爲80000,則一切都會好的。我的意思是,我們仍然有巨大的價值,兩條線都很長,但效果正常。或者我沒有明白你的想法,是嗎? – 2012-07-25 14:09:14

回答

1

使用自定義像素着色器,它必須創建一箇中間位圖,然後該紋理被像素着色器採樣。

您正在創建大量渲染,因此您在渲染路徑中遇到了一些限制。

速戰速決是夾要渲染什麼如下:

Geometry clip = new RectangleGeometry(new Rect(0,0,this.ActualWidth, this.ActualHeight)); 

dc.PushClip(clip); 

dc.DrawLine(new Pen(Brushes.Red, 2), new Point(0, 0), new Point(this.EndX, 100)); 
dc.DrawLine(new Pen(Brushes.Green, 3), new Point(200, 10), new Point(10, 300)); 

dc.Pop(); 

UPDATE:

有一種說法是,它使用過濾器擴展位圖,當它超過了最大紋理尺寸(它可以根據你的顯卡架構而有所不同)...所以它通過不同大小的像素着色器......然後它縮小回原來的大小。

因此,縮放過濾器會根據位圖的內容導致僞像(即水平線和垂直線在比縮放比對角線更好的情況下存在)。

.NET 4將默認過濾器更改爲低質量過濾器...雙線性,而不是幻想...也許這會影響您獲得的質量。

http://10rem.net/blog/2010/05/16/more-on-image-resizing-in-net-4-vs-net-35sp1-bilinear-vs-fant

UPDATE2:

這種證實了我上面想的。

如果您使用Windows性能工具包/套件(Windows SDK的一部分),則可以看到視頻內存被橙色圖形吞噬,同時增加滑塊值,因爲正在創建更大的中間位圖紋理。它一直在增加,直到它達到極限,然後它變平......這就是像素化變得明顯的時候。

enter image description here

UPDATE3:

如果設置渲染模式爲「軟件渲染」(0級),那麼你可以看看它是如何與呈現如此大的視覺科佩斯 - 文物開始出現在一個不同的點....大概是因爲紋理的大小限制是更大/不同於你的GPU。但是這些工件仍然出現,因爲它在內部使用了雙線性過濾器。

嘗試使用RenderOptions.SetBitmapScalingMode將過濾器設置爲Fant似乎沒有以任何方式更改呈現質量(我猜是因爲它在通過自定義像素着色器路徑時不受尊重)。

將這個在Application_Startup看到軟件渲染的結果:

RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly; 
1

請注意,圖像通常在垂直方向上模糊,但在水平方向呈鋸齒狀。

由於着色器應用於柵格圖像而不是矢量,因此線條被柵格化爲紋理。硬件通常支持高達8198 * 8192的紋理。 在我的情況下,「模糊」,如你所說,出現在16384的滑塊值。所以,我的virtualBox虛擬圖形卡支持高達16384 * 16384。 您的限制可能會有所不同。所以只要保持這個值低於那個。

但奇怪的是,WPF柵格化整個圖像,因爲只有一小部分可見。 所以還有另一個可能的原因,那就是在着色器本身,但它被編譯成二進制,所以我無法檢查它。

更新: 在我而言,它看起來是這樣的: with shader applied

看起來是垂直方向而不是水平過濾。

+0

我現在也注意到你的圖像看起來和我的不一樣。您的外觀像低分辨率雙線性過濾。這意味着你有正常的GPU。它使用方形紋理。在我的情況下,圖像只是水平伸展(沒有模糊,因爲我沒有過濾)。 – 2012-07-25 14:22:31

+0

我已向LVL發佈類似評論。如果兩條線都很大 - 效果適用。你能解釋一下嗎?你能上傳你的圖片嗎? – 2012-07-25 14:30:53

1

好吧,我有這個! 我使用灰度效果對庫進行了反編譯,並且還反編譯了WCF PresentationCore庫,以檢查爲什麼BlurEffect在相同的情況下完美工作。 而我發現BlurEffect實現抽象方法Effect.GetRenderBounds這是缺席灰度效果。我還注意到,GrayscaleEffect是針對PresentationCore v 3.0.0構建的,其中效果沒有GetRenderBound

所以這是WPF的第3個和第4個版本之間的不兼容。 有三種方式來解決這個問題:

  1. 如果你有GrayscaleEffect的源代碼 - 添加所需的方法和編譯反對版本4.0.0運行時。
  2. 您可以將應用程序使用的運行時切換到版本3. *。
  3. 如果你沒有的來源GrayscaleEffect,但不能使用運行時的第3版,寫包裝器GrayscaleEffect繼承影響(V4),並實現缺席的方法。

我試過第二種方法,問題就消失了。

+0

_GetRenderBounds_在_ShaderEffect_中實現,它是_GrayscaleEffect_的基類,其實現與_BlurEffect_的實現非常相似。編譯GrayscaleEffect對.NET 4.0沒有幫助。編譯應用程序對.NET 3.5有幫助,但這種方式在我的真實應用程序中是不可接受的。看起來這是框架內部的問題。 – 2012-07-26 18:51:48

0

舊的問題,但可能適用於在應用Custom ShaderEffect之後模糊圖像有問題的人。

另外問題提到的OP可能與渲染內容的比例相關, 從WPFShadersLibrary將ShaderEffects應用到視頻,文本和任何其他正常窗口內容後,模糊問題類似。

我注意到圖像向下移動了一點點,導致「像素分割」,所以我爲選定的ShaderEffect:XOffset和YOffset創建了兩個新屬性,並將它們應用於HLSL(請參閱下面的代碼),然後綁定到滑塊XAML:

float2 newPos; 
newPos.x = uv.x + offsetX; 
newPos.y = uv.y + offsetY; 

然後我嘗試了一些任意的偏移量,並能重新調整畫面。仍然存在一些最小的模糊(或者詳細損失),但結果明顯更好。

目前這個解決方案的問題,我不知道如何根據分辨率或窗口大小預測偏移量。