应用着色器效果后模糊的图片

Ale*_*hik 5 c# wpf pixel-shader

我在WPF中遇到了像素着色器的奇怪行为.

这个问题100%可重复,所以我写了一个小的演示程序.您可以在此处下载源代码.

万恶之源是名为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));
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,此框架元素呈现2行:下面的行具有永久坐标,但上面的行依赖于EndX依赖项属性.

所以这个框架元素是像素着色器效果的目标.为简单起见,我使用此处的灰度着色器效果.所以我将GrayscaleEffect应用于MyFrameworkElement.你可以看到结果,看起来不错.

直到我大幅增加EndX属性.

小线模糊,大线很好!

但是如果我删除灰度效果,所有线条都会看起来应该如此.

任何人都可以解释这种模糊的原因是什么?或者甚至更好我如何解决这个问题?

Col*_*ith 2

使用自定义像素着色器,它必须创建中间位图,然后像素着色器对该纹理进行采样。

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

一个快速解决方法是剪辑您想要渲染的内容,如下所示:

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();
Run Code Online (Sandbox Code Playgroud)

更新:

一种理论是,当位图超过最大纹理大小(这可能会根据您的显卡架构而变化)时,它会使用过滤器来缩放位图......因此它会以不同的大小通过像素着色器......然后它缩放回原始大小。

因此,缩放过滤器会根据位图的内容产生伪影(即水平线和垂直线比对角线更好地在缩小和放大的情况下生存)。

.NET 4 将用于过滤的默认过滤器更改为较低质量的过滤器...双线性,而不是 Fant...也许这也会影响您获得的质量。

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

更新2:

这证实了我上面的想法。

如果您使用 Windows 性能工具包/套件(Windows SDK 的一部分),那么当您增加滑块值时,您可以看到橙色图中的视频内存被吞噬,因为正在创建更大的中间位图纹理。它不断增加,直到达到极限,然后趋于平缓……这就是像素化变得明显的时候。

在此输入图像描述

更新3:

如果将渲染模式设置为“软件渲染器”(第 0 层),那么您可以看到它如何处理渲染如此大的视觉效果 - 伪影开始出现在不同的点......大概是因为纹理大小限制更大/与您的 GPU 不同。但伪影仍然出现,因为它内部使用了双线性滤波器。

尝试使用 RenderOptions.SetBitmapScalingMode 将过滤器升级到 Fant 似乎不会以任何方式改变渲染质量(我猜是因为当它通过自定义像素着色器路径时它不会受到尊重)。

将其放入 Application_Startup 中以查看软件渲染器结果:

RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;
Run Code Online (Sandbox Code Playgroud)