Simple OSX trick to create a transparent OpenGL rendered window


#1

While i was working on a video player i wondered wether i could make my window transparent using the video's alpha channel as a mask. Video Codecs like Prores 4444 or DXV (our own codec) can contain an alpha channel.
So I set my OpenGLAppComponent to be not opaque and made sure i would clear to transparentBlack in the render callback. I actually though it would just work out of the box because with a normal Component this is all you have to do.
What i found out is though that on OSX you need to set the NSOpenGLCPSurfaceOpacity of the native NSOpenGLContext, otherwise it will be always opaque and show black. Once i figured out how to set this flag it worked beatifully. 

So i though i share the code with you. I added a screenshot that demonstrates what you would see if you use the code below and show it on top of a Chrome browser with the Juce.com homepage ;-)

Code:

Usage:

glComponent = new GLComponent();

glComponent->setVisible(true);

glComponent->centreWithSize(800, 600);

glComponent->addToDesktop(0);

GLComponent.h

#include "headers.h"


class GLComponent  : public OpenGLAppComponent

{

public:

    GLComponent ();

    ~GLComponent();


void paint (Graphics& g) override;

void resized() override;

void setOpenGLContextSurfaceOpacityToZero();

void initialise() override;

void shutdown() override;

void render() override;


private:

void drawCube();

void setPerspectiveProjection( float fovy, float aspect, float zNear, float zFar );

};


GLComponent.mm




#include <AppKit/NSOpenGL.h>

#include "GLComponent.h"


void GLComponent::setOpenGLContextSurfaceOpacityToZero()

{

NSOpenGLContext* context = (NSOpenGLContext*)openGLContext.getRawContext();    

GLint aValue = 0;

    [context setValues:&aValue forParameter:NSOpenGLCPSurfaceOpacity];

}

GLComponent.cpp
Just some demonstration code, rendering a few cubes using ancient OpenGL code. 
 

#include "GLComponent.h"


GLComponent::GLComponent()

{

setOpaque (false);

    setSize (800, 600);

}


GLComponent::~GLComponent()

{

}


void GLComponent::paint (Graphics& g)

{

}


void GLComponent::resized()

{

}



void GLComponent::initialise()

{

setOpenGLContextSurfaceOpacityToZero();

}


void GLComponent::shutdown()

{

}


void GLComponent::render()

{

OpenGLHelpers::clear(Colours::transparentBlack);


glMatrixMode( GL_PROJECTION );

glPushMatrix();

setPerspectiveProjection( 45.0f, (float)getWidth() / getHeight(), 0.1, 100.0 );

glMatrixMode( GL_MODELVIEW );

glPushMatrix();

glLoadIdentity();


glPushMatrix();


//OpenGLHelpers::clear();

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

float value = sin(Time::getMillisecondCounter()/1000.0f);

Colour blue = Colours::steelblue;

float x = -1.0;

float y = -1.0;

for (int i=0; i<9;i++)

    {

        glPushMatrix();

        glTranslatef(x, y, -9.0);

        glRotatef(-180.0f + value*360.0f, 0.5, 1.0, 0.0);

        glColor4f(blue.getFloatRed(), blue.getFloatGreen(), blue.getFloatBlue(), 1.0/(float)(i+1));

        drawCube();

        glPopMatrix();


        if (i % 3 == 0)

        {

            x = -1.0;

            y += 1.0;

        } else {

            x += 1.0;

        }

    }

glPopMatrix();

glMatrixMode( GL_PROJECTION );

glPopMatrix();

glMatrixMode( GL_MODELVIEW );

glPopMatrix();


}



void GLComponent::drawCube()

{

static const GLfloat cube_vertices[] =

    {

        -1.0f,-1.0f,-1.0f, // triangle 1 : begin

        -1.0f,-1.0f, 1.0f,

        -1.0f, 1.0f, 1.0f, // triangle 1 : end

        1.0f, 1.0f,-1.0f, // triangle 2 : begin

        -1.0f,-1.0f,-1.0f,

        -1.0f, 1.0f,-1.0f, // triangle 2 : end

        1.0f,-1.0f, 1.0f,

        -1.0f,-1.0f,-1.0f,

        1.0f,-1.0f,-1.0f,

        1.0f, 1.0f,-1.0f,

        1.0f,-1.0f,-1.0f,

        -1.0f,-1.0f,-1.0f,

        -1.0f,-1.0f,-1.0f,

        -1.0f, 1.0f, 1.0f,

        -1.0f, 1.0f,-1.0f,

        1.0f,-1.0f, 1.0f,

        -1.0f,-1.0f, 1.0f,

        -1.0f,-1.0f,-1.0f,

        -1.0f, 1.0f, 1.0f,

        -1.0f,-1.0f, 1.0f,

        1.0f,-1.0f, 1.0f,

        1.0f, 1.0f, 1.0f,

        1.0f,-1.0f,-1.0f,

        1.0f, 1.0f,-1.0f,

        1.0f,-1.0f,-1.0f,

        1.0f, 1.0f, 1.0f,

        1.0f,-1.0f, 1.0f,

        1.0f, 1.0f, 1.0f,

        1.0f, 1.0f,-1.0f,

        -1.0f, 1.0f,-1.0f,

        1.0f, 1.0f, 1.0f,

        -1.0f, 1.0f,-1.0f,

        -1.0f, 1.0f, 1.0f,

        1.0f, 1.0f, 1.0f,

        -1.0f, 1.0f, 1.0f,

        1.0f,-1.0f, 1.0f

    };

glEnableClientState( GL_VERTEX_ARRAY );

glVertexPointer(3, GL_FLOAT, 0, &cube_vertices[0] );

glDrawArrays(GL_TRIANGLES, 0, 12*3);

glDisableClientState( GL_VERTEX_ARRAY );

}



void GLComponent::setPerspectiveProjection( float fovy, float aspect, float zNear, float zFar )

{

RA_GLCONTEXT;

glMatrixMode( GL_PROJECTION );

glLoadIdentity();

// This code is based off the MESA source for gluPerspective

// *NOTE* This assumes GL_PROJECTION is the current matrix

float xmin, xmax, ymin, ymax;

float m[ 16 ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

#define M(x,y) m[((y)<<2)+(x)]

    ymax = zNear * tan( fovy * PI / 360.0 );

    ymin = -ymax;

    xmin = ymin * aspect;

    xmax = ymax * aspect;

// Set up the projection matrix

M( 0, 0 ) = ( 2.0 * zNear ) / ( xmax - xmin ); //proj00 =

M( 1, 1 ) = ( 2.0 * zNear ) / ( ymax - ymin ); //proj11 =

M( 2, 2 ) = -( zFar + zNear ) / ( zFar - zNear );

M( 0, 2 ) = ( xmax + xmin ) / ( xmax - xmin );

M( 1, 2 ) = ( ymax + ymin ) / ( ymax - ymin );

M( 3, 2 ) = -1.0;

M( 2, 3 ) = -( 2.0 * zFar * zNear ) / ( zFar - zNear );

#undef M

// Add to current matrix

glMultMatrixf( m );

glMatrixMode( GL_MODELVIEW );

}

 

 

 


#2

Whatever i try i can't seem to be able to upload a screenshot, it's just a jpeg and the size around 500kb. Any clue what might be wrong? 

 


#3

In the meantime i made screenrecording demonstrating the transparency in my video player.
​https://vimeo.com/138754354


#4

Nice work, looks great


#5

Looks cool :-)


#6

yeah, wicked!