DrawingContext的PushEffect已弃用,.NET 4.7.2下如何动态添加自定义Shader特效?
.NET Framework 4.7.2自定义控件应用Shader Model 3+特效的解决方案
问题背景
我制作了一个自定义控件用于处理带特效的合成图像渲染,希望为控件内特定元素应用自定义Shader特效,但原本便捷的内置PushEffect和PopEffect函数已被弃用且无实际作用。
为实现需求,我创建了新的DrawingVisual并在其上绘制图像,再为该Visual应用特效,这一步看似可行。但将其绘制到控件时需通过RenderTargetBitmap,而该类采用软件渲染,无法支持Shader Model 3及以上版本的自定义特效,导致特效丢失。
核心问题
- 是否存在无需通过
RenderTargetBitmap,就能让OnRender中的DrawingContext直接绘制另一个上下文内容的方法?或是无需先转为图像即可合并多个绘图上下文的方式?我了解DrawingGroup,但不确定如何在OnRender中使用。
注:希望通过后台代码实现,XAML方案帮助不大,也欢迎提供其他可能遗漏的、为自定义控件特定元素应用特效的方法。运行环境为.NET Framework 4.7.2。
可行解决方案
方案1:直接将DrawingVisual加入控件可视化树
不需要把DrawingVisual转成Bitmap,直接将其添加到控件的可视化子元素中,让WPF的硬件渲染管线处理特效,完美支持Shader Model 3+:
修改后的控件代码:
public class CustomControl : FrameworkElement { private readonly DrawingVisual _drawingVisual = new DrawingVisual(); public CustomControl() { // 初始化DrawingVisual的绘制内容 using (var dc = _drawingVisual.RenderOpen()) { Rect rect = new Rect(new Point(0, 0), new Size(200, 400)); dc.DrawRectangle(Brushes.LightBlue, null, rect); } // 应用自定义特效 _drawingVisual.Effect = new CustomEffect(); } // 重写可视化子元素计数 protected override int VisualChildrenCount => 1; // 返回指定索引的可视化子元素 protected override Visual GetVisualChild(int index) { if (index != 0) throw new ArgumentOutOfRangeException(nameof(index)); return _drawingVisual; } protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); // 无需再绘制Bitmap,DrawingVisual已在可视化树中自动渲染 // 若有其他额外绘制需求,可在此继续添加 } }
方案2:使用DrawingGroup合并绘图内容
如果不想修改可视化树,可以用DrawingGroup合并绘图内容并应用特效,直接绘制到OnRender的上下文。不过要注意,这种方式依赖硬件加速是否开启(默认开启,若系统不支持会 fallback 到软件渲染,仍会丢失特效):
protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); var drawingGroup = new DrawingGroup(); // 在DrawingGroup中绘制内容 using (var dc = drawingGroup.Open()) { Rect rect = new Rect(new Point(0, 0), new Size(200, 400)); dc.DrawRectangle(Brushes.LightBlue, null, rect); } // 给DrawingGroup应用自定义特效 drawingGroup.Effect = new CustomEffect(); // 直接将DrawingGroup绘制到当前上下文 drawingContext.DrawDrawing(drawingGroup); }
关键提示
RenderTargetBitmap采用软件渲染,这是它不支持Shader Model 3+特效的核心原因,必须避开这种方式。- 方案1是最可靠的实现方式,因为可视化树中的元素会由WPF硬件渲染管线处理,只要显卡支持Shader Model 3+就能正常显示特效。
- 若必须在
OnRender中合并多个绘图内容,可优先尝试方案2,若特效仍丢失则回到方案1。
内容的提问来源于stack exchange,提问作者Cody Hubbard
相关产品推荐
相关产品推荐

