Janela parcialmente transparente OpenGL / Win32 - c ++, winapi, opengl

Eu li todas as perguntas existentes sobre este assunto complicado (nicho), mas estou preso. Eu tenho uma janela do Win32 com um contexto OpenGL. Eu quero que minha janela seja parcialmente transparente.

Meu resultado até agora é que a totalidade dojanela é transparente. Eu quero apenas a área preta para ser transparente, para que eu possa desenhar alguns objetos 3D e eles vão parecer que estão vindo da janela.

insira a descrição da imagem aqui

Primeiro, na minha aula de janela, defini o hbrBackground como preto.

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);

Eu criei minha janela com o sinalizador 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);

No meu formato de pixel, eu habilitei alpha e composição.

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;

Eu tentei a região de borrão "truque", mas não tem efeito. Meu resultado não está relacionado a este pedaço de código.

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);

Por fim, defini o LWA_COLORKEY eAtributos LWA_ALPHA. Isso é o que me deu o efeito exibido. No entanto, a chave de cores não parece ser levada em conta (tentei também valores diferentes de zero).

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

Eu não esqueci de habilitar a mistura.

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

Respostas:

1 para resposta № 1

O que você deseja fazer requer a composição de janelas que existe desde o Windows Vista, portanto, essencialmente, todas as versões do Windows com as quais você precisa se preocupar (o Windows XP e versões anteriores são EoL).

Os principais passos a seguir são: habilitar a composição intra-janela do DWM, habilitando "Blue Behind Window" e usar um WM_POPUP janela; se você não usar WM_POPUP estilo o gerenciador de janelas irá desenhar decorações e sua renderização OpenGL irá "pairar" acima disso.

        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);

Em seguida, você deve criar um contexto OpenGL usando a API "attribute" mais recente, em vez de usar a seleção do descritor pixelformat. Com a API de atributo, você pode selecionar um transparente com alfa formato de janela.

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 formatn");
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 formatn");
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);

return NULL;
}

Eu tenho um exemplo completo de trabalho para isso na minha wglarb repositório do wrapper no GitHub: https://github.com/datenwolf/wglarb/blob/master/test/layered.c


2 para resposta № 2

Você pode estar faltando a chamada para UpdateLayeredWindow . IIRC, não chamando UpdateLayeredWindow resulta em algum comportamento estranho. YMMV.

Mais detalhes Aqui


0 para resposta № 3

De acordo com SetLayeredWindowAttributes doc você precisa passar

uma estrutura COLORREF que especifica ochave de cores de transparência para ser usado ao compor a janela em camadas. Todos os pixels pintados pelo janela nesta cor será transparente. Para gerar um COLORREF, use a macro RGB.

Revise o segundo parâmetro da sua chamada para esta função.