Как нарисовать прозрачную поверхность с помощью SharpDX?

(Этот вопрос основан на дальнейших исследованиях этот другой вопрос, но это не тот же вопрос, это очень конкретный вопрос о проблемах с покраской.)

Я пытаюсь нарисовать прозрачную поверхность, наложенную на целевое окно, проблема в том, что я не знаю, как нарисовать ее прозрачной, поэтому к тому моменту, когда моя поверхность стала черной, я не вижу, как правильно очистить черный цвет этой поверхности в коде ниже.

Я читал о форматах пикселей и альфа-режимах, однако, похоже, я не могу использовать 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

Приведенный выше код представляет собой адаптацию VB.NET принятого ответа this вопрос.


person ElektroStudios    schedule 23.06.2016    source источник
comment
Вот ссылка, на которую стоит посмотреть - stackoverflow.com/questions/26646715/   -  person AGrammerPro    schedule 27.06.2016
comment
Спасибо за комментарий и за попытку помочь. Я также нашел этот вопрос, но я думаю (я думаю), что он не применим к этому сценарию, потому что он предназначен для создания прозрачной формы / объекта, а не поверхности. Честно говоря, я не знаю, как протестировать / воспроизвести этот пример.   -  person ElektroStudios    schedule 27.06.2016
comment
Глядя на эту страницу MSDN кажется, что Straight альфа-режим не поддерживается при работе с ID2D1HwndRenderTarget интерфейсом (который, я полагаю, использует SharpDX).   -  person Visual Vincent    schedule 29.06.2016
comment
Чтобы прозрачность работала, как я писал в своем ответе, вам нужно расширить аэродинамическое стекло, чтобы покрыть всю форму DwmExtendFrameIntoClientArea, и использовать черный цвет в качестве цвета фона вашей формы. Это работает с DirectX, поэтому будет работать и с SharpDX.   -  person γηράσκω δ' αεί πο    schedule 30.06.2016
comment
@ γηράσκω δ 'αεί πολλά διδασκόμε Если вы познакомитесь с DirectX, вы наверняка сможете иметь гораздо больше идей, чем я, используя SharpDx; возможно, я сделал что-то не так, но я использовал DwmExtendFrameIntoClientArea с правильным размером в соответствующей структуре margins, и я все еще вижу черный фон в целевом окне. Используемый мной тип рендеринга hwnd, похоже, не имеет опции / свойства для установки цвета по умолчанию ... или я не могу его найти, поэтому я не могу проверить, что цвет черный, но я пытаюсь очистить черный цвет, чтобы увидеть черную поверхность.   -  person ElektroStudios    schedule 01.07.2016
comment
@Visual Vincent, я вижу ... но тогда?. Я очень запутался, потому что, если это ограничение означает, что я должен использовать интерфейс ID2D1BitmapRenderTarget или ID2D1RenderTarget вместо этого, тогда конструкторы класса оболочки SharpDX (BitmapRenderTargetandWicRenderTarget я полагаю) принимает в качестве параметра другой (базовый ) RenderTarget class !!, поэтому я пытаюсь создать экземпляр класса, который наследуется от RenderTarget, но его конструктор запрашивает у меня другой экземпляр RenderTarget? Я этого не понимаю; также я могу передать собственный указатель, но я не знаю, что он означает в этом значении DirectX. Я полностью потерялся.   -  person ElektroStudios    schedule 01.07.2016
comment
@ γηράσκω δ 'αεί πολλά διδασκόμε Я не использую свою форму для размещения ее над целевым окном, которым вообще управляет HwndRenderTarget (насколько я понимаю). Причина, по которой я хотел бы перейти с XNA на Sharp DX, заключается в том, что XNA устарела, а SharpDX, с другой стороны, продолжает развиваться; а также во избежание взлома окон Win32 в отношении DWM / GDI / Transparency, которые негативно повлияют на общую производительность моего приложения, потому что я предполагаю, что если бы я мог воспроизвести то же самое только с помощью DirectX, то это было бы быстрее и эффективнее.   -  person ElektroStudios    schedule 01.07.2016
comment
Собственный указатель - это просто указатель на экземпляр ID2D1HwndRenderTarget. В VB.NET/C# это будет IntPtr.   -  person Visual Vincent    schedule 01.07.2016
comment
Вы не можете рисовать прозрачно в другом окне (я видел это в вашем коде). Вам нужна прозрачная форма (окно) сверху игры. Он очень четко говорит об этом на странице TurboHUD: рисовать в прозрачном окне Direct2D (он не работает в полноэкранном режиме, только в оконном и оконном (полноэкранном))   -  person γηράσκω δ' αεί πο    schedule 01.07.2016
comment
Сделайте свою форму самой верхней и используйте свои формы, а не блокноты. Помните, у вашей формы не должно быть границ. PS Мне тоже сложно понять SharpDX. DirectX очень прост для базовых вещей, если, конечно, вы знакомы с синтаксисом C C ++.   -  person γηράσκω δ' αεί πο    schedule 01.07.2016
comment
@ γηράσκω δ 'αεί πολλά διδασκόμε Спасибо за комментарий, тогда я попробую поместить форму в самый верхний и посмотреть, добьюсь ли я этого наконец (я имею в виду использование SharpDX). Я не знаком с языками C / C ++, но у меня нет проблем с пониманием кода с использованием (не расширенного) синтаксиса.   -  person ElektroStudios    schedule 03.07.2016
comment
Вы нашли свой путь с SharpDX? Если нет, я предлагаю вам использовать GDI. Если вы не собираетесь рисовать тысячи линий или сотни больших изображений, тогда GDI будет достаточно быстрым, чтобы не повлиять на производительность.   -  person γηράσκω δ' αεί πο    schedule 05.07.2016
comment
@ γηράσκω δ 'αεί πολλά διδασκόμε Да, я наконец решил это (лучше сказать, что вы решили это !!). Может быть, вы видели код, который я опубликовал в своем ответе? Я хотел бы убедиться, что все делаю правильно, не теряя производительности. Если в любой момент вы захотите опубликовать ответ в этой ветке с более оптимизированным кодом (на C # или VB.NET, неважно), я приму ответ.   -  person ElektroStudios    schedule 08.07.2016


Ответы (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
person ElektroStudios    schedule 08.07.2016