在DirectX内容上叠加WinForms控件

时间:2013-12-12 12:04:25

标签: wpf winforms directx transparency gdi

我正在尝试使用图形控件创建图形库。该控件可以添加叠加控件。控件上的图形使用DirectX并且是库的一部分,而叠加控件由库的最终用户提供,而不是图形专家。因此,稳定性和方便的API是最重要的问题。

由于技术原因,我需要直接在控件上显示DirectX的图形,并且不能使用另一个框架作为WPF来托管DirectX场景(有关更多信息,请参阅我之前的问题:Stereoscopic 3D on WPF)。

我认为最棘手的问题是:

  • 控件本身区域内的透明度(和半透明度......)。
  • 叠加控件上的动画:这就是为什么控制光栅化(例如通过WPF)不是一个选项。

我的尝试直到现在:

  1. 在大量帖子讨论了类似的问题后,我决定使用WS_EX_TRANSPARENT。我很惊讶地看到虽然Airspace issue,我可以在叠加控件的透明区域下看到DirectX内容。然而,除了我调整窗口大小时,叠加控制没有显示 - 然后它会闪烁并再次消失。以下是叠加控件的代码:

    class GDIGraphicsControl : UserControl
    {
      private const int WS_EX_TRANSPARENT = 0x20;
    
      protected override CreateParams CreateParams
      {
        get
        {
          CreateParams p = base.CreateParams;
          p.ExStyle |= WS_EX_TRANSPARENT;
          return p;
        }
      }
    
      public GDIGraphicsControl()
      {
        this.Dock = DockStyle.Fill;
      }
    
      protected override void OnPaintBackground(PaintEventArgs e)
      {
        // Do nothing
      }
    
      protected override void OnPaint(PaintEventArgs e)
      {
        for (int i = 0; i < 10; ++i)
        {
          int alpha = 25 * i;
          int yPos = 10 * i;
          e.Graphics.FillRectangle(
            new SolidBrush(Color.FromArgb(alpha, Color.Green)),
            5, 5 + yPos, 100, 10);
        }
        e.Graphics.FillEllipse(new SolidBrush(Color.Red), 110, 5, 100, 100);
      }
    }
    
  2. 为了分析眨眼问题,我试图让事情更简单。首先出于调试目的,我尝试使用GDI渲染而不是DirectX。我实现它,使它与DirectX渲染非常相似 - 尤其是每个OnPaint()中的Form.SetStyle和Invalidate()调用,我认为这是问题的原因。在仅GDI渲染中也存在问题。这是GDI渲染父控制代码:

    public partial class RenderingForm : Form
    {
      public RenderingForm()
      {
        InitializeComponent();
    
        ControlStyles styles =
          ControlStyles.AllPaintingInWmPaint |
          ControlStyles.OptimizedDoubleBuffer |
          ControlStyles.ResizeRedraw |
          ControlStyles.UserPaint |
          ControlStyles.Opaque;
        this.SetStyle(styles, true);
    
        Button button = new Button()
        {
          Text = "Just a button",
          Left = 5,
          Top = 210,
          Width = 200
        };
    
        this.Controls.Add(button);
        this.Controls.Add(new GDIGraphicsControl());
      }
    
      protected override void OnPaintBackground(PaintEventArgs e)
      {
        // Do nothing
      }
    
      protected override void OnPaint(PaintEventArgs e)
      {
        e.Graphics.Clear(Color.Blue);
    
        this.Invalidate();
    
        base.OnPaint(e);
      }
    }
    
  3. 我尝试添加一个简单的叠加按钮,以检查在没有使用透明度时是否存在问题(参见上面的代码)。该按钮不闪烁,但是我在屏幕上看到垃圾而不是按钮,直到第一次调整控件的大小 - 然后按钮才会正确显示。

  4. 我尝试使子叠加控件无效,但它没有效果。我试图在父的OnPaint和子OnPaint中使它无效,以创建消息泵渲染效果,它没有任何效果。像在父渲染控件中一样设置控件的样式并不能解决问题并导致背景以黑色闪烁。

  5. 我还使用WPF进行了一些尝试,但问题已经足够长了,现在没有硬Airspace issue ......

    现在提出问题:

    1. 有人可以解释GDI覆盖DirectX图形的工作原理吗?特别是我不理解它,因为我知道WinForms透明度正在工作,子控件在父设备上下文中呈现 - 而DirectX具有硬件渲染上下文。是否意味着将DirectX纹理复制回软件?

    2. 为什么覆盖CreateParams会导致控件闪烁以及如何防止它?

    3. 这些透明度方法的性能成本是什么(覆盖CreateParams,BackColorTransparency = True,Control.SetStyle +覆盖OnPainBackground)?非常重要的是,底层图形将被有效渲染,但我不关心叠加性能(轻量级动画除外)。

1 个答案:

答案 0 :(得分:1)

我在这方面做了很多研究,我得出的结论是,空域是最佳选择。我从Web上的众多项目中收集了资源,并将它们放入一个允许您将WPF与XNA集成的项目中。我还做了一个纯粹的DirectX版本,但说实话,XNA版本更直接,更适合C#。我没有足够的空间在这里发布所有细节,但你可以download the demo from my website并可能想出去哪里。祝你好运!