2012-07-25 12 views
5

Ich bin auf seltsames Verhalten des Pixel-Shaders in WPF gestoßen.Unscharfes Bild nach dem Anwenden des Shader-Effekts

Dieses Problem ist 100% reproduzierbar, so schrieb ich kleines Demo-Programm. Sie können den Quellcode here herunterladen.

Die Wurzel allen Übels ist winzig Klasse betitelt 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)); 
    } 
} 

Wie Sie dieses Rahmenelement macht 2 Zeilen sehen: untere Linie permanent Koordinaten aber obere Linie hängt von EndX Abhängigkeitseigenschaft hat.

Also dieses Framework-Element ist Ziel für Pixel-Shader-Effekt. Der Einfachheit halber verwende ich Graustufen-Shader-Effekt gefunden here. Also habe ich GrayscaleEffect auf MyFrameworkElement angewendet. Sie können das Ergebnis sehen, es sieht gut aus.

Bis ich drastisch erhöhen EndX Eigenschaft.

Kleine Linie ist verschwommen und große Linie ist in Ordnung!

Aber wenn ich Graustufeneffekt entfernen, werden alle Zeilen so aussehen, wie sie sollten.

Kann jemand erklären, was ist der Grund dafür zu verwischen? Oder noch besser, wie kann ich dieses Problem lösen?

+0

Scheint wie eine Einschränkung eines BitmapEffect für große Werte. – LPL

+0

@LPL Wenn Sie die zweite (untere) Zeile an die EndX-Eigenschaft binden und dann EndX auf 80000 setzen, ist alles in Ordnung. Ich meine, wir haben immer noch riesige Werte, beide Linien sind sehr lang, aber der Effekt funktioniert richtig. Oder ich habe deine Idee nicht verstanden, oder? –

Antwort

1

Mit einem benutzerdefinierten Pixel-Shader muss ein Intermediate Bitmap erstellt werden, und dann wird diese Textur vom Pixelshader abgetastet.

Sie erstellen eine massive Rendering, so dass Sie einige Einschränkungen im Render-Pfad treffen.

Eine schnelle Lösung ist zu befestigen, was Sie gemacht werden sollen, wie folgt:

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:

Eine Theorie ist, dass sie ein Filter ist mit der Bitmap skaliert, wenn es um die maximale Größe Textur überschreitet (das kann je nach Grafikkartenarchitektur variieren) ... also geht es durch den Pixel Shader in einer anderen Größe .... dann wird es wieder auf Originalgröße skaliert.

So verursacht der Skalierungsfilter abhängig vom Inhalt Ihrer Bitmap Artefakte (d. H. Horizontale Linien und vertikale Linien überstehen eine horizontale und vertikale Skalierung besser als diagonale Linien).

.NET 4 hat den Standardfilter für die Filterung auf eine niedrigere Qualität geändert ... Bilinear, statt Fant ... vielleicht beeinflusst dies die Qualität, die Sie auch bekommen.

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

UPDATE2:

Diese Art der bestätigt, was ich oben dachte.

Wenn Sie das Windows Performance Toolkit/Suite (Teil von Windows SDK) verwenden, können Sie den Videospeicher in der orangefarbenen Grafik sehen, während Sie den Schiebereglerwert erhöhen, da eine größere Zwischenbitmap-Textur erstellt wird. Es nimmt zu, bis es ein Limit erreicht, dann flacht es ab ... und das ist, wenn die Pixelierung offensichtlich wird.

enter image description here

UPDATE3:

Wenn Sie den Modus auf „Software Renderer“ machen gesetzt (Tier 0), dann können Sie sehen, wie es mit Rendering eine so große visuelle meistert - die Artefakte beginnen erscheinen an ein anderer Punkt ... vermutlich, weil die Grenze der Texturgröße größer/anders als bei Ihren GPUs ist. Die Artefakte werden jedoch weiterhin angezeigt, da intern ein Bilinear-Filter verwendet wird.

Der Versuch, RenderOptions.SetBitmapScalingMode zu verwenden, um den Filter auf Fant hochzufahren, scheint die Wiedergabequalität in keiner Weise zu ändern (ich nehme an, es wird nicht beachtet, wenn es den benutzerdefinierten Pixel-Shader-Pfad durchläuft).

Setzen Sie dieses in Application_Startup die Software-Renderer Ergebnisse zu sehen:

RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly; 
1

Beachten Sie, dass das Bild normalerweise in vertikaler Richtung unscharf ist, aber horizontal ist.

Da Shader auf Rasterbilder und nicht auf Vektoren angewendet werden, werden die Linien in Textur gerastert. Hardware unterstützt normalerweise Texturen bis zu 8198 * 8192. In meinem Fall erscheint die "Unschärfe", wie Sie es nennen, bei einem Schiebereglerwert von 16384. Also unterstützt meine virtuelle virtualBox Grafikkarte bis zu 16384 * 16384. Ihr Limit kann abweichen. Also behalte diesen Wert niedriger als das.

Aber es ist seltsam, dass WPF das gesamte Bild rastert, da nur ein kleiner Teil davon sichtbar ist. Es gibt also noch einen weiteren möglichen Grund, der im Shader selbst liegt, aber er wird in Binärdateien kompiliert, so dass ich ihn nicht überprüfen kann.

Update: In meinem Fall sieht es so aus: with shader applied

Sieht aus wie es vertikal gefiltert wird, aber nicht horizontal.

+0

Mir ist jetzt auch aufgefallen, dass dein Bild anders aussieht als mein. Ihr Aussehen wird als niedrige Auflösung bilinear gefiltert. Das heißt, Sie haben normale GPU. Und es verwendet quadratische Texturen. In meinem Fall wurde das Bild nur horizontal gestreckt (nicht verschwommen, da ich keine Filtration habe). –

+0

Ich habe bereits ähnliche Kommentare zu LVL geschrieben. Wenn beide Linien riesig sind, gilt der Effekt richtig. Kannst du es erklären? Können Sie Ihre Bilder irgendwo hochladen? –

1

Ok, ich habe das bekommen! Ich dekompilierte die Bibliothek mit Ihrem Graustufen-Effekt und dekompilierte WCF PresentationCore-Bibliothek, um zu überprüfen, warum BlurEffect funktioniert perfekt in der gleichen Situation. Und ich fand, dass BlurEffect implementiert abstrakte Methode Effect.GetRenderBounds die abwesend ist in GrayscaleEffect. Ich habe auch bemerkt, dass GrayscaleEffect gegen PresentationCore v 3.0.0 erstellt wurde, wo der Effekt GetRenderBound nicht hat.

Das ist also eine Inkompatibilität zwischen der 3. und 4. Version von WPF. Es gibt drei Möglichkeiten, es zu beheben:

  1. Wenn Sie Quellcode GrayscaleEffect haben - benötigt Methoden hinzufügen und es gegen 4.0.0 Version Laufzeit kompilieren.
  2. Sie können die Laufzeit, die Ihre Anwendung verwendet, auf Version 3 umstellen. *.
  3. Wenn Sie nicht über Quellen GrayscaleEffect kann aber nicht 3. Version der Laufzeit verwenden, schreiben Wrapper für GrayscaleEffect die Effect (v4) und implementiert fehlen Methoden erbt.

Ich versuchte 2nd Way und das Problem verschwand.

+0

_GetRenderBounds_ ist in _ShaderEffect_ implementiert, was die Basisklasse von _GrayscaleEffect_ ist, und seine Implementierung ähnelt der Implementierung von _BlurEffect_. Das Kompilieren von GrayscaleEffect gegen .NET 4.0 hilft nicht. Kompilieren der Anwendung gegen .NET 3.5 hilft, aber dieser Weg ist in meiner realen Anwendung inakzeptabel. Sieht so aus als wäre dies ein Problem innerhalb des Frameworks. –

0

alte Frage, aber könnte für jemanden nützlich sein, der Probleme mit der Unschärfe des Bildes nach dem Anwenden von Custom ShaderEffect hat.

Auch Problem OP erwähnt möglicherweise auf Skala von gerenderten Inhalten, , Ich hatte ähnliches Problem mit Unschärfe nach dem Anwenden von ShaderEffects von WPFShadersLibrary auf Video, Text und andere Inhalte in normalen Fenster.

Was ich gemerkt habe, dass das Bild um ein kleines Bit nach unten verschoben, was zu "Pixel Splitting", so habe ich zwei neue Eigenschaften für ausgewählte ShaderEffect: XOffset und YOffset, und wandte sie in HLSL (siehe Code unten), dann binded zu Sliders in XAML:

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

Dann habe ich mit einigen beliebigen Offsets experimentiert und konnte das Bild neu auszurichten. Es gibt immer noch einige minimale Unschärfe (oder Verlust im Detail), aber das Ergebnis war deutlich besser.

Problem mit dieser Lösung derzeit, dass ich nicht weiß, wie Offset je nach Auflösung oder Fenstergröße vorhersagen.

Verwandte Themen