【问题标题】:How to draw a transparent surface using SharpDX?如何使用 SharpDX 绘制透明表面?
【发布时间】:2016-06-23 12:07:52
【问题描述】:

(此问题基于对this其他问题的进一步调查,但不是同一个问题,这是关于绘画问题的非常具体的问题。)

我正在尝试绘制一个重叠在目标窗口上的透明表面,问题是我不知道如何将它涂成透明,所以当我的表面是黑色的时候,我看不到正确的方法来在下面的代码中清除该表面的黑色。

我读过有关像素格式和 alphamodes 的信息,但是,我似乎无法使用据称用于允许透明度的 AlphaMode.Straight

我知道一个可以做到这一点的免费软件应用程序,它的名字是TurboHUD(一个在游戏客户端窗口上绘制透明表面以绘制对象的应用程序,即,一个HUD)。老实说,也许很荒谬:我从两年多前就开始尝试实现这一点,我仍然不知道如何通过透明度来开始做到这一点,我需要开始在透明表面上绘制对象。

我做错了什么?此示例代码是用 VB.NET 编写的,但我也接受 C# 中的解决方案。

Imports SharpDX
Imports SharpDX.Direct2D1
Imports SharpDX.Direct3D
Imports SharpDX.DXGI
Imports SharpDX.Mathematics.Interop
Imports SharpDX.Windows

Public NotInheritable Class Form1 : Inherits Form

    Private factory As New Direct2D1.Factory(Direct2D1.FactoryType.SingleThreaded)
    Private render As WindowRenderTarget
    Private renderProps As HwndRenderTargetProperties
    Private renderThread As Thread = Nothing

    Private Sub Form1_Load() Handles MyBase.Shown

        Dim hwnd As IntPtr = Process.GetProcessesByName("notepad").Single().MainWindowHandle

        Me.renderProps = New HwndRenderTargetProperties()
        Me.renderProps.Hwnd = hwnd
        Me.renderProps.PixelSize = New Size2(1920, 1080)
        Me.renderProps.PresentOptions = PresentOptions.None

        Me.render = New WindowRenderTarget(Me.factory, New RenderTargetProperties(New PixelFormat(Format.B8G8R8A8_UNorm, Direct2D1.AlphaMode.Premultiplied)), Me.renderProps)

        Me.renderThread = New Thread(New ParameterizedThreadStart(AddressOf Me.DoRender))
        Me.renderThread.Priority = ThreadPriority.Normal
        Me.renderThread.IsBackground = True
        Me.renderThread.Start()

    End Sub

    Private Sub DoRender(ByVal sender As Object)

        While True
            Me.render.BeginDraw()
            ' Me.render.Clear(New RawColor4(0, 0, 0, 0))
            Me.render.Clear(SharpDX.Color.Transparent)
            Me.render.Flush()
            Me.render.EndDraw()
        End While

    End Sub

End Class

上面的代码是对this问题的公认答案的VB.NET改编。

【问题讨论】:

  • 这里有一个链接值得一看-stackoverflow.com/questions/26646715/…
  • 感谢您的评论和帮助。我也发现了这个问题,但我认为(我认为)不适用于这种情况,因为那是为了使形状/对象透明,而不是表面。老实说,我不知道如何测试/重现该示例。
  • this MSDN page似乎在处理ID2D1HwndRenderTarget接口(我猜SharpDX正在使用)时不支持Straight alpha模式。
  • 不能在不同的窗口上透明地绘制(我在你的代码中看到了)。您需要在游戏的 top 上有一个透明的表单(窗口)。它在 TurboHUD 页面中说得很清楚:“在透明的 Direct2D 窗口上绘画(它不能在“全屏”模式下工作,只能在“窗口化”和“窗口化(全屏)”下工作)"
  • 你找到了sharpDX的方法吗?如果不是,我建议你使用 GDI。如果您不打算绘制数千条线或数百张大图像,那么 GDI 足够快,不会影响性能。

标签: c# .net vb.net directx sharpdx


【解决方案1】:

非常感谢@γηράσκω δ' αεί πολλά διδασκόμε 的建议,我终于使用 SharpDx 完成了这项工作。

下面的代码包含一些对外部库的调用,但是我认为这个想法会很清楚。

正如@γηράσκω δ' αεί πολλά διδασκόμε所说,要使用WindowRenderTarget 似乎需要以我自己的形式使用它,并且我的形式必须满足这些条件:

  • 背景颜色为黑色。
  • 做一个无边界的表单。
  • 成为最顶层的窗口(显而易见)。
  • 必须通过调用DwmExtendFrameIntoClientArea函数将窗口框架扩展到客户区。

然后我可以调用方法WindowRenderTarget.Clear(Color.Transparent) 使表单透明。请注意,Clear() 方法不适用于具有上述条件的我们自己的窗体以外的任何其他窗口,这意味着如果我们尝试直接在目标窗口上绘制透明表面而不是使用我们的窗体来执行此操作,我们会产生一个不能透明的纯色表面(我真的不明白为什么不能。)

所以在完成了所有提到的基本步骤之后,现在将只是一个润色的事情,比如将源窗口重叠在目标窗口的顶部(以产生 HUD 效果),处理目标窗口调整大小,以及你想要的。下面的代码只是演示性的,目前我还没有很好地处理这些事情。

代码如下:

Imports D2D1 = SharpDX.Direct2D1
Imports D3D = SharpDX.Direct3D
Imports DXGI = SharpDX.DXGI

Imports DxColor = SharpDX.Color
Imports DxPoint = SharpDX.Point
Imports DxRectangle = SharpDX.Rectangle
Imports DxSize = SharpDX.Size2

Imports Device = SharpDX.Direct3D11.Device
Imports MapFlags = SharpDX.Direct3D11.MapFlags

Imports Elektro.Imaging.Tools
Imports Elektro.Interop.Win32
Imports Elektro.Interop.Win32.Enums
Imports Elektro.Interop.Win32.Types

Public NotInheritable Class Form1 : Inherits Form

    <DllImport("dwmapi.dll")>
    Private Shared Function DwmExtendFrameIntoClientArea(ByVal hwnd As IntPtr, ByRef margins As Margins) As Integer
    End Function

    Private factory As New D2D1.Factory(D2D1.FactoryType.SingleThreaded)
    Private render As D2D1.WindowRenderTarget
    Private renderProps As D2D1.HwndRenderTargetProperties
    Private renderThread As Thread = Nothing

    Private srcHwnd As IntPtr
    Private dstHwnd As IntPtr

    Private Sub Form1_Load() Handles MyBase.Shown

        ' Window handles of source and target window.
        Me.srcHwnd = Me.Handle
        Me.dstHwnd = Process.GetProcessesByName("notepad").Single().MainWindowHandle

        ' Form settings.
        Me.BackColor = Color.Black
        Me.FormBorderStyle = FormBorderStyle.None
        Me.TopMost = True

        ' DWM stuff for later to be able make transparent the source window.
        Dim rc As NativeRectangle ' a win32 RECT
        NativeMethods.GetClientRect(srcHwnd, rc)
        Dim margins As Margins
        margins.TopHeight = rc.Width
        margins.BottomHeight = rc.Height
        DwmExtendFrameIntoClientArea(srcHwnd, margins)
        ' ------------------------------------------------

        Me.renderProps = New D2D1.HwndRenderTargetProperties()
        Me.renderProps.Hwnd = srcHwnd
        Me.renderProps.PixelSize = New DxSize(rc.Width, rc.Height)
        Me.renderProps.PresentOptions = D2D1.PresentOptions.None

        Me.render = New D2D1.WindowRenderTarget(Me.factory, New D2D1.RenderTargetProperties(New D2D1.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D1.AlphaMode.Premultiplied)), Me.renderProps)

        Me.renderThread = New Thread(New ParameterizedThreadStart(AddressOf Me.DoRender))
        Me.renderThread.Priority = ThreadPriority.Normal
        Me.renderThread.IsBackground = True
        Me.renderThread.Start()

    End Sub

    Private Sub DoRender(ByVal sender As Object)

        While True
            Me.OverlapToWindow(Me.srcHwnd, Me.dstHwnd)
            Me.render.BeginDraw()
            Me.render.Clear(DxColor.Transparent)
            Me.render.Flush()
            Me.render.EndDraw()
        End While

    End Sub

    Private Sub OverlapToWindow(ByVal srcHwnd As IntPtr, ByVal dstHwnd As IntPtr)
        ' Gets the (non-client) Rectangle of the windows, taking into account a borderless window of Windows 10.
        Dim srcRect As Rectangle = ImageUtil.GetRealWindowRect(srcHwnd)
        Dim dstRect As Rectangle = ImageUtil.GetRealWindowRect(dstHwnd)

        NativeMethods.SetWindowPos(srcHwnd, dstHwnd,
                                   dstRect.X, dstRect.Y, dstRect.Top, dstRect.Left,
                                   SetWindowPosFlags.IgnoreZOrder Or SetWindowPosFlags.IgnoreResize)
    End Sub

End Class

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-09
    • 1970-01-01
    • 2013-04-05
    • 2010-09-16
    • 2016-09-23
    • 1970-01-01
    相关资源
    最近更新 更多