【问题标题】:Partially transparent window OpenGL/Win32部分透明窗口OpenGL/Win32
【发布时间】:2018-07-08 18:15:41
【问题描述】:

我已经阅读了关于这个棘手(利基)主题的所有现有问题,但我被困住了。我有一个带有 OpenGL 上下文的 Win32 窗口。我希望我的窗口部分透明。

到目前为止,我的结果是整个窗口都是透明的。我只希望黑色区域是透明的,这样我就可以绘制一些 3D 对象,它们看起来就像是从窗户里出来的一样。

首先,在我的窗口类中,我将 hbrBackground 设置为黑色。

Windows::WindowClass windowClass;
windowClass.cbSize = sizeof(Windows::WindowClass);
windowClass.style = Windows::CS_VREDRAW | Windows::CS_HREDRAW;
windowClass.lpfnWndProc = WndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = moduleHandle;
windowClass.hIcon = Windows::LoadIcon(0u, (X*)Windows::IDI_QUESTION);
windowClass.hCursor = Windows::LoadCursor(0u, (X*)Windows::IDC_ARROW);
windowClass.hbrBackground = Windows::CreateSolidBrush(0x00000000);
windowClass.lpszMenuName = nullptr;
windowClass.lpszClassName = (X*)name;
windowClass.hIconSm = Windows::LoadIcon(0u, (X*)Windows::IDI_QUESTION);

我创建了带有 WS_EX_LAYERED 标志的窗口。

windowHandle = Windows::CreateWindow(Windows::WS_EX_LAYERED, (X*)name, "", Windows::WS_POPUP, w / 4, h / 4, w / 2, h / 2, 0u, 0u, moduleHandle, 0u);

在我的像素格式中,我启用了 alpha 和合成。

PixelFormatDescriptor format;
format.nSize = sizeof(PixelFormatDescriptor);
format.nVersion = 1;
format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER | PFD_SUPPORT_COMPOSITION;
format.iPixelType = PFD_TYPE_RGBA;
format.cColorBits = 32;
format.cRedBits = 0;
format.cRedShift = 0;
format.cGreenBits = 0;
format.cGreenShift = 0;
format.cBlueBits = 0;
format.cBlueShift = 0;
format.cAlphaBits = 8;
format.cAlphaShift = 0; 
format.cAccumBits = 0; 
format.cAccumRedBits = 0;
format.cAccumGreenBits = 0;
format.cAccumBlueBits = 0;
format.cAccumAlphaBits = 0;
format.cDepthBits = 24;
format.cStencilBits = 8; 
format.cAuxBuffers = 0; 
format.iLayerType = PFD_MAIN_PLANE;
format.bReserved = 0;
format.dwLayerMask = 0;
format.dwVisibleMask = 0; 
format.dwDamageMask = 0;

我尝试过模糊区域“技巧”,但没有效果。我的结果与这段代码无关。

struct DwmBlurBehind
{
    U4 dwFlags;
    S4 fEnable;
    X* blurRegionHandle;
    S4 fTransitionOnMaximized;
};

DwmBlurBehind blurBehind;
blurBehind.dwFlags = Windows::DWM_BB_ENABLE | Windows::DWM_BB_BLURREGION;
blurBehind.fEnable = true;
blurBehind.blurRegionHandle = Windows::CreateRectRgn(0, 0, -1, -1);
blurBehind.fTransitionOnMaximized = false;
Windows::DwmEnableBlurBehindWindow(windowHandle, &blurBehind);

最后,我设置了 LWA_COLORKEY 和 LWA_ALPHA 属性。这就是给我展示的效果。但是,似乎没有考虑颜色键(我也尝试过非零值)。

Windows::SetLayeredWindowAttributes(windowHandle, 0, 170, Windows::LWA_COLORKEY | Windows::LWA_ALPHA);

我没有忘记启用混合。

GL::Enable(GL::BLEND);
GL::BlendFunc(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA);

【问题讨论】:

  • 有一个high-performance technique 禁用了通常在绘制到窗口时使用的重定向表面。它涉及设置WS_EX_NOREDIRECTION 扩展窗口样式并调用CreateSwapChainForComposition() 以创建直接呈现给窗口组合模块的交换链。您正在使用 OpenGL,但也许您可以调整此方法。
  • OpenGL 混合与 Windows 中的窗口子系统实现的半透明完全无关。 OpenGL 中的 Alpha 混合在 OpenGL 渲染上下文处结束。

标签: c++ winapi opengl


【解决方案1】:

您可能错过了对 UpdateLayeredWindow 的调用。 IIRC,不调用 UpdateLayeredWindow 会导致一些奇怪的行为。 YMMV。

更多详情here

【讨论】:

  • 到目前为止还没有工作。我仍在尝试不同的参数组合,这个函数有很多东西。谢谢你的线索。
  • 愚蠢的问题,但我真的需要有多个窗口才能使用分层吗?我只有一个,这可能是个问题。
  • 另外,我不确定是应该在每一帧上调用 UpdateLayeredWindow 还是只在创建后调用一次。
  • 很久没有做分层窗口了。我会这样做。首先尝试一个没有 OpenGL 的基本空窗口,看看你是否可以让它成为半透明的。然后将 OpenGL 添加回来。您可能需要在承载 OpenGL 的主框架内创建一个子窗口(外部框架是分层的,但内部不是)。
【解决方案2】:

您想要做的事情需要自 Windows Vista 以来一直存在的窗口合成,因此基本上您必须关心的每个 Windows 版本(Windows XP 和更早版本都是 EoL)。

要采取的关键步骤是,通过启用“Blue Behind Window”并使用WM_POPUP 窗口来启用DWM 窗口内合成;如果您不使用WM_POPUP 样式,则窗口管理器将绘制装饰,并且您的 OpenGL 渲染将“悬停”在其上方。

        DWM_BLURBEHIND bb = {0};
        bb.dwFlags = DWM_BB_ENABLE;
        bb.fEnable = TRUE;
        bb.hRgnBlur = NULL;
        DwmEnableBlurBehindWindow(hWnd, &bb);

        MARGINS margins = {-1};
        impl_DwmExtendFrameIntoClientArea(hWnd, &margins);

接下来,您必须使用较新的“属性”API 创建 OpenGL 上下文,而不是使用像素格式描述符选择。使用属性 API,您可以选择具有 alpha 窗口格式的 透明

int attribs[] = {
    WGL_DRAW_TO_WINDOW_ARB, TRUE,
    WGL_DOUBLE_BUFFER_ARB, TRUE,
    WGL_SUPPORT_OPENGL_ARB, TRUE, 
    WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
    WGL_TRANSPARENT_ARB, TRUE,
    WGL_COLOR_BITS_ARB, 32,
    WGL_RED_BITS_ARB, 8,
    WGL_GREEN_BITS_ARB, 8,
    WGL_BLUE_BITS_ARB, 8,
    WGL_ALPHA_BITS_ARB, 8,
    WGL_DEPTH_BITS_ARB, 24,
    WGL_STENCIL_BITS_ARB, 8,
    0, 0
};

INT iPF;
UINT num_formats_choosen;
if( !wglChoosePixelFormatARB(
        hDC, 
        attribs, 
        NULL,
        1,
        &iPF,
        &num_formats_choosen) ) {
    fprintf(stderr, "error choosing proper pixel format\n");
    return NULL;
}
if( !num_formats_choosen ) {
    return NULL;
}

PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(pfd));
/* now this is a kludge; we need to pass something in the PIXELFORMATDESCRIPTOR 
 * to SetPixelFormat; it will be ignored, mostly. OTOH we want to send something
 * sane, we're nice people after all - it doesn't hurt if this fails. */
DescribePixelFormat(hDC, iPF, sizeof(pfd), &pfd);

if( !SetPixelFormat(hDC, iPF, &pfd) ) {
    fprintf(stderr, "error setting proper pixel format\n");
    ReleaseDC(hWnd, hDC);
    DestroyWindow(hWnd);

    return NULL;
}

我在 GitHub 上的 wglarb 包装器存储库中有一个完整的工作示例:https://github.com/datenwolf/wglarb/blob/master/test/layered.c

【讨论】:

    【解决方案3】:

    根据SetLayeredWindowAttributes doc你需要通过

    一个 COLORREF 结构,指定透明度颜色键为 组成分层窗口时使用。绘制的所有像素 这种颜色的窗口将是透明的。要生成 COLORREF,请使用 RGB 宏。

    查看调用此函数的第二个参数。

    【讨论】:

    • 对于黑色,宏无论如何都会产生 0。 COLORREF 是一个 DWORD (uint32_t),格式为 0x00bbggrr。我不喜欢抽象,所以我摆脱了它。 :)
    猜你喜欢
    • 1970-01-01
    • 2011-05-31
    • 1970-01-01
    • 2020-07-12
    • 2012-03-18
    • 1970-01-01
    • 1970-01-01
    • 2011-04-27
    • 1970-01-01
    相关资源
    最近更新 更多