Fenêtre partiellement transparente OpenGL / Win32 - c ++, winapi, opengl

J'ai lu toutes les questions existantes sur ce sujet délicat, mais je suis coincé. J'ai une fenêtre Win32 avec un contexte OpenGL. Je veux que ma fenêtre soit partiellement transparente.

Mon résultat jusqu'à présent est que l'intégralité dula fenêtre est transparente. Je veux seulement que la zone noire soit transparente, de sorte que je puisse dessiner quelques objets 3D et ils auront l'air de sortir de la fenêtre.

entrez la description de l'image ici

Tout d'abord, dans ma classe de fenêtre, j'ai défini hbrBackground en noir.

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

J'ai créé ma fenêtre avec le drapeau 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);

Dans mon format de pixel, j'ai activé l'alpha et la composition.

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;

J'ai essayé le "truc" de la région de flou, mais cela n'a aucun effet. Mon résultat n'est pas lié à ce morceau de code.

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

Finally, I have set the LWA_COLORKEY and LWA_ALPHA attributs. C'est ce qui m'a donné l'effet affiché. Cependant, la clé de couleur ne semble pas être prise en compte (j'ai aussi essayé des valeurs non nulles).

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

Je n'ai pas oublié de permettre le mélange.

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

Réponses:

1 pour la réponse № 1

Ce que vous voulez faire nécessite la composition de fenêtres qui existe depuis Windows Vista, donc essentiellement toutes les versions de Windows dont vous avez à vous soucier (Windows XP et versions antérieures sont EoL).

Les principales étapes à suivre sont les suivantes: activer la composition intra-fenêtre DWM en activant "Blue Behind Window" et utiliser un WM_POPUP fenêtre; si vous n'utilisez pas WM_POPUP style le gestionnaire de fenêtres dessine des décorations et votre rendu OpenGL "planera" au-dessus de cela.

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

Ensuite, vous devez créer un contexte OpenGL en utilisant l'API "attribute" plus récente au lieu d'utiliser la sélection de descripteur pixelformat. Avec l'API d'attribut, vous pouvez sélectionner un transparent avec alpha format de fenêtre.

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

J'ai un exemple de travail complet pour cela dans mon wglarb référentiel wrapper sur GitHub: https://github.com/datenwolf/wglarb/blob/master/test/layered.c


2 pour la réponse № 2

Vous pourriez manquer l'appel à Mettre à jourLayeredWindow . IIRC, n'appelant pas UpdateLayeredWindow entraîne un comportement étrange. YMMV.

Plus de détails ici


0 pour la réponse № 3

Selon SetLayeredWindowAttributes doc vous devez passer

une structure COLORREF qui spécifieclé de couleur de transparence à être utilisé lors de la composition de la fenêtre en couches. Tous les pixels peints par le La fenêtre de cette couleur sera transparente. Pour générer un COLORREF, utilisez la macro RGB.

Passez en revue le deuxième paramètre de votre appel à cette fonction.