c# - 应用着色器效果后图片模糊

标签 c# wpf pixel-shader

我遇到过 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 属性。

小线模糊,大线还好!

但是如果我去掉灰度效果,所有的线条都会看起来像它们应该的样子。

谁能解释一下这种模糊的原因是什么? 或者更好的是我该如何解决这个问题?

最佳答案

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

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

一个快速的解决方法是按如下方式剪辑您想要呈现的内容:

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();

更新:

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

因此,缩放滤镜会根据您的位图内容导致伪影(即水平线和垂直线比对角线更能在缩小和放大时幸存下来)。

.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 Performance Toolkit/Suite(Windows SDK 的一部分),那么当您增加 slider 值时,您可以在橙色图表中看到视频内存被吞噬,因为正在创建更大的中间位图纹理。它不断增加,直到达到极限,然后变平...这就是像素化变得明显的时候。

enter image description here

更新 3:

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

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

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

RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;

关于c# - 应用着色器效果后图片模糊,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11648256/

相关文章:

c# - 使用 Dapper 的 SQLite。分析列时出错。无法将 System.Int64 类型的对象转换为 System.Double 类型

c# - Winforms/WPF 应用程序支持彩色表情符号吗?

c# - WPF MVVM textBox 文本绑定(bind)

position - 在像素着色器中计算世界空间坐标

java - 有没有适用于 JAVA 的 HLSL 或 HYDRA Pixel Shader 语言?

c# - 如何在 UI 中对计数器值进行动画处理以增加 C# WPF 中的值?

C# System.InvalidCastException 异常

C# 正则表达式总是返回 FALSE

C# (WPF) : Database Relationship Viewer

xna - 在哪里调用 SetRenderTarget?