Sök…


Anmärkningar

OpenGL är en öppen standard för rendering av grafisk hårdvara för 2D- och 3D-grafik. OpenGL har implementerats över en fantastisk uppsättning plattformar som gör att appar som är inriktade på OpenGL är extremt flexibla.

versioner

Version Utgivningsdatum
1,1 1997/03/04
1,2 1998-03-16
1.2.1 1998/10/14
1,3 2001-08-14
1,4 2002/07/24
1,5 2003-07-29
2,0 2004-09-07
2,1 2006-07-02
3,0 2008-08-11
3,1 2009-03-24
3,2 2009-08-03
3,3 2010-03-11
4,0 2010-03-11
4,1 2010-07-26
4,2 2011-08-08
4,3 2012-08-06
4,4 2013/07/22
4,5 2014/08/11

Få OpenGL

En av de vanligaste missuppfattningarna om OpenGL är att det var ett bibliotek som kan installeras från tredje parts källor. Denna missuppfattning leder till många frågor i formen "hur installerar jag OpenGL" eller "var ska jag ladda ner OpenGL SDK".

Så här hittar inte OpenGL vägen in i datorsystem. OpenGL i sig är bara en uppsättning specifikationer för vilka kommandon en implementering måste följa. Så det är genomförandet som betyder något. Och för tillfället är OpenGL-implementationer en del av GPU-drivrutinerna. Detta kan förändras i framtiden, när nya GPU-programmeringsgränssnitt tillåter att verkligen implementera OpenGL som ett bibliotek, men för tillfället är det ett programmerings-API mot grafikdrivrutinerna.

När OpenGL först släppte API hittade han på något sätt in i ABI (Application Binary Interface) kontraktet för Windows, Solaris och Linux (LSB-4 Desktop) utöver sitt ursprung Sun Irix. Apple följde och integrerade faktiskt OpenGL så djupt i MacOS X, att den tillgängliga OpenGL-versionen är tätt kopplad till versionen av MacOS X installerad. Detta har den anmärkningsvärda effekten, att systemprogrammeringsmiljöer för dessa operativsystem (dvs. kompilator- och länkverktygskedjan som nativt riktar sig till dessa system) måste leverera även OpenGL API-definitioner. Sådant är det inte nödvändigt att installera en SDK för OpenGL. Det är tekniskt möjligt att programmera OpenGL på dessa operativsystem utan krav på att installera en dedicerad SDK, förutsatt att en byggmiljö som följer den riktade ABI är installerad.

En biverkning av dessa strikta ABI-regler är att OpenGL-versionen som exponeras genom bindningsgränssnittet är en lägsta gemensamma nämnare som program som körs på målplattformen kan förvänta sig att vara tillgängliga. Därför måste moderna OpenGL-funktioner nås via förlängningsmekanismen, som beskrivs i djupet separat.

Linux

I Linux är det ganska vanligt att fackutveckla utvecklingspaketen för olika aspekter av systemet, så att dessa kan uppdateras individuellt. I de flesta Linux-distributioner finns utvecklingsfilerna för OpenGL i ett dedicerat paket, det är vanligtvis ett beroende för ett metapaket för ett utvecklingsprogram för skrivbordsapplikationer. Så installationen av OpenGL-utvecklingsfilerna för Linux tas vanligtvis hand om med installationen av metapaketet för skrivbordsutveckling. *

Microsoft Windows

API-bindningsbiblioteket opengl32.dll (benämnd så för både 32-bitars- och 64-bitarsversioner av Windows) levereras som standard med alla Windows-versioner sedan Windows NT-4 och Windows 95B (båda cirka 1997). Denna DLL tillhandahåller emellertid inte en verklig OpenGL-implementering (bortsett från en programvarufall som endast syftar till att fungera som ett säkerhetsnät för program om ingen annan OpenGL-implementering är installerad). Denna DLL tillhör Windows och får inte ändras eller flyttas! Moderna OpenGL-versioner levereras som en del av den så kallade Installable Client Driver (ICD) och nås via standard opengl32.dll som levereras förinstallerad med alla Windows-versioner. Internt beslutades dock av Microsoft att grafikdrivrutiner installerade via Windows Update inte skulle installera / uppdatera en OpenGL ICD. Eftersom sådana nya installationer av Windows med drivrutiner installerade automatiskt saknar stöd för moderna OpenGL-funktioner. För att få en OpenGL ICD med moderna funktioner måste grafikdrivrutiner laddas ner direkt från GPU-leverantörens webbplats och installeras manuellt.

När det gäller utveckling måste inga extra åtgärder vidtas i sig. Alla C / C ++ -kompilatorer som följer Windows ABI-specifikationerna levereras med rubriker och länkstubben (opengl32.lib) som krävs för att bygga och länka körbara filer som använder sig av OpenGL.

Manuell OpenGL-inställning på Windows

Fullständig exempelkod ingår i slutet

Windows-komponenter för OpenGL

WGL

WGL (kan uttalas wiggle ) står för "Windows-GL", som i "ett gränssnitt mellan Windows och OpenGL" - en uppsättning funktioner från Windows API för att kommunicera med OpenGL. WGL-funktioner har ett wgl- prefix och dess symboler har ett WGL_- prefix.

Standard OpenGL-version som stöds på Microsoft-system är 1.1. Det är en mycket gammal version (den senaste är 4,5). Sättet att få de senaste versionerna är att uppdatera dina grafikdrivrutiner, men ditt grafikkort måste stödja de nya versionerna.

Hela listan över WGL-funktioner finns här .

Grafiskt enhetsgränssnitt (GDI)

GDI (idag uppdaterad till GDI +) är ett 2D-ritgränssnitt som låter dig rita på ett fönster i Windows. Du behöver GDI för att initiera OpenGL och låta det interagera med det (men kommer faktiskt inte använda GDI själv).

I GDI har varje fönster en enhetskontekst (DC) som används för att identifiera ritmålet när du ringer funktioner (du passerar det som en parameter). OpenGL använder dock sin egen renderingskontext (RC) . Så DC kommer att användas för att skapa RC.


Grundläggande installation

Skapa ett fönster

Så för att göra saker i OpenGL behöver vi RC och för att få RC behöver vi DC och för att få DC behöver vi ett fönster. Att skapa ett fönster med Windows API kräver flera steg. Detta är en grundläggande rutin, så för en mer detaljerad förklaring bör du konsultera annan dokumentation, eftersom det inte handlar om att använda Windows API.

Detta är en Windows-installation, så Windows.h måste inkluderas, och programmets ingångspunkt måste vara WinMain proceduren med dess parametrar. Programmet måste också vara länkat till opengl32.dll och till gdi32.dll (oavsett om du är på 64 eller 32 bitars system).

Först måste vi beskriva vårt fönster med WNDCLASS strukturen. Den innehåller information om fönstret vi vill skapa:

/* REGISTER WINDOW */
WNDCLASS window_class;

// Clear all structure fields to zero first
ZeroMemory(&window_class, sizeof(window_class));

// Define fields we need (others will be zero)
window_class.style = CS_OWNDC;
window_class.lpfnWndProc = window_procedure; // To be introduced later
window_class.hInstance = instance_handle;
window_class.lpszClassName = TEXT("OPENGL_WINDOW");

// Give our class to Windows
RegisterClass(&window_class);
/* *************** */

För en exakt förklaring av innebörden av varje fält (och för en fullständig lista över fält), se MSDN-dokumentation.

Sedan kan vi skapa ett fönster med CreateWindowEx . När fönstret har skapats kan vi förvärva dess likström:

/* CREATE WINDOW */
HWND window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                                    TEXT("OPENGL_WINDOW"),
                                    TEXT("OpenGL window"),
                                    WS_OVERLAPPEDWINDOW,
                                    0, 0,
                                    800, 600,
                                    NULL,
                                    NULL,
                                    instance_handle,
                                    NULL);

HDC dc = GetDC(window_handle);

ShowWindow(window_handle, SW_SHOW);
/* ************* */

Slutligen måste vi skapa en meddelandeslinga som tar emot fönsterhändelser från OS:

/* EVENT PUMP */
MSG msg;

while (true) {
    if (PeekMessage(&msg, window_handle, 0, 0, PM_REMOVE)) {
        if (msg.message == WM_QUIT)
            break;
        
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    // draw(); <- there goes your drawing

    SwapBuffers(dc); // To be mentioned later
}
/* ********** */

Pixelformat

OpenGL behöver veta lite information om vårt fönster, till exempel färgbit, buffertmetod och så vidare. För detta använder vi ett pixelformat . Vi kan emellertid bara föreslå för operativsystemet vilken typ av pixelformat vi behöver, och operativsystemet kommer att leverera den mest likadana stödda formaten , vi har inte direkt kontroll över det. Det är därför det bara kallas en deskriptor .

/* PIXEL FORMAT */
PIXELFORMATDESCRIPTOR descriptor;

// Clear all structure fields to zero first
ZeroMemory(&descriptor, sizeof(descriptor));

// Describe our pixel format
descriptor.nSize = sizeof(descriptor);
descriptor.nVersion = 1;
descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_GENERIC_ACCELERATED | PFD_DOUBLEBUFFER | PFD_SWAP_LAYER_BUFFERS;
descriptor.iPixelType = PFD_TYPE_RGBA;
descriptor.cColorBits = 32;
descriptor.cRedBits = 8;
descriptor.cGreenBits = 8;
descriptor.cBlueBits = 8;
descriptor.cAlphaBits = 8;
descriptor.cDepthBits = 32;
descriptor.cStencilBits = 8;

// Ask for a similar supported format and set it
int pixel_format = ChoosePixelFormat(dc, &descriptor);
SetPixelFormat(dc, pixel_format, &descriptor);
/* *********************** */

Vi har aktiverat dubbel buffring i dwFlags fältet, så vi måste ringa SwapBuffers för att se saker efter ritning.

Framförande kontext

Efter det kan vi helt enkelt skapa vårt renderingskontekst:

/* RENDERING CONTEXT */
HGLRC rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
/* ***************** */

Observera att endast en tråd kan använda RC-enheten åt gången. Om du vill använda den från en annan tråd senare, måste du ringa wglMakeCurrent där för att aktivera den igen (detta kommer att inaktivera den på den tråd som den är aktiv och så vidare).

Få OpenGL-funktioner

OpenGL-funktioner erhålls med hjälp av funktionspekare. Det allmänna förfarandet är:

  1. På något sätt få funktionen pekartyper (i huvudsak funktionen prototyper)
  2. Förklara varje funktion som vi vill använda (med dess funktion pekartyp)
  3. Få den faktiska funktionen

Tänk till exempel glBegin:

// We need to somehow find something that contains something like this,
// as we can't know all the OpenGL function prototypes
typedef void (APIENTRY *PFNGLBEGINPROC)(GLenum);

// After that, we need to declare the function in order to use it
PFNGLBEGINPROC glBegin;

// And finally, we need to somehow make it an actual function

("PFN" betyder "pekaren att fungera", följer sedan namnet på en OpenGL-funktion och "PROC" i slutet - det är det vanliga namnet på pekartypen för OpenGL-funktionen.)

Så här görs det på Windows. Som tidigare nämnts skickar Microsoft endast OpenGL 1.1. Först kan funktionspekartyper för den versionen hittas genom att inkludera GL/gl.h Därefter förklarar vi alla funktioner som vi tänker använda som visas ovan (genom att göra det i en huvudfil och förklara dem "extern" skulle vi kunna använda dem alla efter att ha laddat dem en gång, bara genom att inkludera det). Slutligen laddas OpenGL 1.1-funktionerna genom att öppna DLL:

HMODULE gl_module = LoadLibrary(TEXT("opengl32.dll"));

/* Load all the functions here */
glBegin = (PFNGLBEGINPROC)GetProcAddress("glBegin");
// ...
/* *************************** */

FreeLibrary(gl_module);

Men vi förmodligen vill ha lite mer än OpenGL 1.1. Men Windows ger oss inte funktionen prototyper eller exporterade funktioner för någonting ovanför. Prototyperna måste anskaffas från OpenGL-registret . Det finns tre filer av intresse för oss: GL/glext.h , GL/glcorearb.h och GL/wglext.h .

För att komplettera GL/gl.h från Windows behöver vi GL/glext.h . Den innehåller (som beskrivs av registret) "OpenGL 1.2 och högre kompatibilitetsprofil och tilläggsgränssnitt" (mer om profiler och tillägg senare, där vi ser att det faktiskt inte är en bra idé att använda dessa två filer ).

De faktiska funktionerna måste erhållas av wglGetProcAddress (inget behov av att öppna DLL för den här killen, de är inte där, använd bara funktionen). Med det kan vi hämta alla funktioner från OpenGL 1.2 och senare (men inte 1.1). Observera att för att det ska fungera korrekt måste OpenGL-renderingskontext skapas och göras aktuellt . Så till exempel glClear :

// Include the header from the OpenGL registry for function pointer types

// Declare the functions, just like before
PFNGLCLEARPROC glClear;
// ...

// Get the function
glClear = (PFNGLCLEARPROC)wglGetProcAddress("glClear");

Vi kan faktiskt bygga en wrapper get_proc procedur som använder både wglGetProcAddress och GetProcAddress :

// Get function pointer
void* get_proc(const char *proc_name)
{
    void *proc = (void*)wglGetProcAddress(proc_name);
    if (!proc) proc = (void*)GetProcAddress(gl_module, proc_name); // gl_module must be somewhere in reach

    return proc;
}

Så för att samla in skulle vi skapa en rubrikfil full av funktionspekerdeklarationer som denna:

extern PFNGLCLEARCOLORPROC glClearColor;
extern PFNGLCLEARDEPTHPROC glClearDepth;
extern PFNGLCLEARPROC glClear;
extern PFNGLCLEARBUFFERIVPROC glClearBufferiv;
extern PFNGLCLEARBUFFERFVPROC glClearBufferfv;
// And so on...

Vi kan sedan skapa en procedur som load_gl_functions som vi bara kallar en gång, och fungerar så:

glClearColor = (PFNGLCLEARCOLORPROC)get_proc("glClearColor");
glClearDepth = (PFNGLCLEARDEPTHPROC)get_proc("glClearDepth");
glClear = (PFNGLCLEARPROC)get_proc("glClear");
glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)get_proc("glClearBufferiv");
glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)get_proc("glClearBufferfv");

Och du är redo! Inkludera bara rubriken med funktionspekarna och GL bort.


Bättre inställning

OpenGL-profiler

OpenGL har utvecklats i över 20 år, och utvecklarna var alltid stränga om bakåtkompatibilitet (BC) . Det är mycket svårt att lägga till en ny funktion. År 2008 delades det således upp i två "profiler". Kärna och kompatibilitet . Kärnprofilen bryter BC till förmån för prestandaförbättringar och några av de nya funktionerna. Det tar till och med bort vissa gamla funktioner. Kompatibilitetsprofil upprätthåller BC med alla versioner ner till 1.0, och vissa nya funktioner finns inte tillgängliga på den. Det ska endast användas för gamla, gamla system, alla nya applikationer bör använda kärnprofilen.

Därför finns det ett problem med vår grundläggande installation - den ger bara det sammanhang som är bakåtkompatibelt med OpenGL 1.0. Pixelformatet är också begränsat. Det finns ett bättre sätt att använda tillägg.

OpenGL-tillägg

Alla tillägg till den ursprungliga funktionen i OpenGL kallas förlängningar. Generellt sett kan de antingen göra vissa saker lagliga som inte var förut, utvidga parametervärdeområdet, utöka GLSL och till och med lägga till helt ny funktionalitet.

Det finns tre huvudgrupper av tillägg: leverantör, EXT och ARB. Leverantörstillägg kommer från en specifik leverantör, och de har ett leverantörsspecifikt märke, som AMD eller NV. EXT-tillägg görs av flera leverantörer som arbetar tillsammans. Efter en tid kan de bli ARB-tillägg, som alla är officiellt stödda och som godkänts av ARB.

För att skaffa funktionspekartyper och funktionsprototyper av alla tillägg och som nämnts tidigare, alla funktionspekartyper från OpenGL 1.2 och högre , måste man ladda ner rubrikfilerna från OpenGL-registret . Som diskuterat är det bättre för nya applikationer att använda kärnprofil, så det skulle vara att föredra att inkludera GL/glcorearb.h istället för GL/gl.h och GL/glext.h (om du använder GL/glcorearb.h så don inkluderar inte GL/gl.h ).

Det finns också tillägg för WGL, i GL/wglext.h . Exempelvis är funktionen för att få listan med alla stödda tillägg faktiskt en tillägg i sig, wglGetExtensionsStringARB (den returnerar en stor sträng med en mellanseparerad lista med alla stödda tillägg).

Att få tillägg hanteras också via wglGetProcAddress , så vi kan bara använda vårt omslag som tidigare.

Avancerat pixelformat och kontextskapning

Förlängningen WGL_ARB_pixel_format tillåter oss skapandet av avancerat pixelformat. Till skillnad från tidigare använder vi inte en struktur. Istället passerar vi listan över önskade attribut.

int pixel_format_arb;
UINT pixel_formats_found;

int pixel_attributes[] = {
    WGL_SUPPORT_OPENGL_ARB, 1,
    WGL_DRAW_TO_WINDOW_ARB, 1,
    WGL_DRAW_TO_BITMAP_ARB, 1,
    WGL_DOUBLE_BUFFER_ARB, 1,
    WGL_SWAP_LAYER_BUFFERS_ARB, 1,
    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, 32,
    WGL_STENCIL_BITS_ARB, 8,
    WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
    WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
    0
};

BOOL result = wglChoosePixelFormatARB(dc, pixel_attributes, NULL, 1, &pixel_format_arb, &pixel_formats_found);

På liknande WGL_ARB_create_context tillåter WGL_ARB_create_context tillägget oss avancerad kontextskapning:

GLint context_attributes[] = {
    WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
    WGL_CONTEXT_MINOR_VERSION_ARB, 3,
    WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
    0
};

HGLRC new_rc = wglCreateContextAttribsARB(dc, 0, context_attributes);

För en exakt förklaring av parametrarna och funktionerna, se OpenGL-specifikationen.

Varför började vi inte bara med dem? Det beror på att tilläggene tillåter oss att göra detta, och för att få tillägg behöver vi wglGetProcAddress , men det fungerar bara med ett aktivt giltigt sammanhang. Så i grund och botten, innan vi kan skapa det sammanhang vi vill ha, måste vi redan ha ett visst sammanhang aktivt, och det kallas vanligtvis ett dummy-sammanhang .

Windows tillåter dock inte att ställa in pixelformat i ett fönster mer än en gång. Därför måste fönstret förstöras och återskapas för att tillämpa nya saker:

wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
ReleaseDC(window_handle, dc);
DestroyWindow(window_handle);

// Recreate the window...

Fullständig exempelkod:

/* We want the core profile, so we include GL/glcorearb.h. When including that, then
   GL/gl.h should not be included.

   If using compatibility profile, the GL/gl.h and GL/glext.h need to be included.

   GL/wglext.h gives WGL extensions.

   Note that Windows.h needs to be included before them. */

#include <cstdio>
#include <Windows.h>
#include <GL/glcorearb.h>
#include <GL/wglext.h>

LRESULT CALLBACK window_procedure(HWND, UINT, WPARAM, LPARAM);
void* get_proc(const char*);

/* gl_module is for opening the DLL, and the quit flag is here to prevent
   quitting when recreating the window (see the window_procedure function) */

HMODULE gl_module;
bool quit = false;

/* OpenGL function declarations. In practice, we would put these in a
   separate header file and add "extern" in front, so that we can use them
   anywhere after loading them only once. */

PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB;
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
PFNGLGETSTRINGPROC glGetString;

int WINAPI WinMain(HINSTANCE instance_handle, HINSTANCE prev_instance_handle, PSTR cmd_line, int cmd_show) {
    /* REGISTER WINDOW */
    WNDCLASS window_class;

    // Clear all structure fields to zero first
    ZeroMemory(&window_class, sizeof(window_class));

    // Define fields we need (others will be zero)
    window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    window_class.lpfnWndProc = window_procedure;
    window_class.hInstance = instance_handle;
    window_class.lpszClassName = TEXT("OPENGL_WINDOW");

    // Give our class to Windows
    RegisterClass(&window_class);
    /* *************** */
        
    /* CREATE WINDOW */
    HWND window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                                        TEXT("OPENGL_WINDOW"),
                                        TEXT("OpenGL window"),
                                        WS_OVERLAPPEDWINDOW,
                                        0, 0,
                                        800, 600,
                                        NULL,
                                        NULL,
                                        instance_handle,
                                        NULL);
        
    HDC dc = GetDC(window_handle);
        
    ShowWindow(window_handle, SW_SHOW);
    /* ************* */
        
    /* PIXEL FORMAT */
    PIXELFORMATDESCRIPTOR descriptor;
        
    // Clear all structure fields to zero first
    ZeroMemory(&descriptor, sizeof(descriptor));
        
    // Describe our pixel format
    descriptor.nSize = sizeof(descriptor);
    descriptor.nVersion = 1;
    descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_GENERIC_ACCELERATED | PFD_DOUBLEBUFFER | PFD_SWAP_LAYER_BUFFERS;
    descriptor.iPixelType = PFD_TYPE_RGBA;
    descriptor.cColorBits = 32;
    descriptor.cRedBits = 8;
    descriptor.cGreenBits = 8;
    descriptor.cBlueBits = 8;
    descriptor.cAlphaBits = 8;
    descriptor.cDepthBits = 32;
    descriptor.cStencilBits = 8;
        
    // Ask for a similar supported format and set it
    int pixel_format = ChoosePixelFormat(dc, &descriptor);
    SetPixelFormat(dc, pixel_format, &descriptor);
    /* *********************** */
        
    /* RENDERING CONTEXT */
    HGLRC rc = wglCreateContext(dc);
    wglMakeCurrent(dc, rc);
    /* ***************** */

    /* LOAD FUNCTIONS (should probably be put in a separate procedure) */
    gl_module = LoadLibrary(TEXT("opengl32.dll"));

    wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)get_proc("wglGetExtensionsStringARB");
    wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)get_proc("wglChoosePixelFormatARB");
    wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)get_proc("wglCreateContextAttribsARB");
    glGetString = (PFNGLGETSTRINGPROC)get_proc("glGetString");
    
    FreeLibrary(gl_module);
    /* ************** */

    /* PRINT VERSION */
    const GLubyte *version = glGetString(GL_VERSION);
    printf("%s\n", version);
    fflush(stdout);
    /* ******* */

    /* NEW PIXEL FORMAT*/
    int pixel_format_arb;
    UINT pixel_formats_found;
    
    int pixel_attributes[] = {
        WGL_SUPPORT_OPENGL_ARB, 1,
        WGL_DRAW_TO_WINDOW_ARB, 1,
        WGL_DRAW_TO_BITMAP_ARB, 1,
        WGL_DOUBLE_BUFFER_ARB, 1,
        WGL_SWAP_LAYER_BUFFERS_ARB, 1,
        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, 32,
        WGL_STENCIL_BITS_ARB, 8,
        WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
        WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
        0
    };

    BOOL result = wglChoosePixelFormatARB(dc, pixel_attributes, NULL, 1, &pixel_format_arb, &pixel_formats_found);

    if (!result) {
        printf("Could not find pixel format\n");
        fflush(stdout);
        return 0;
    }
    /* **************** */

    /* RECREATE WINDOW */
    wglMakeCurrent(dc, NULL);
    wglDeleteContext(rc);
    ReleaseDC(window_handle, dc);
    DestroyWindow(window_handle);
    
    window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                                        TEXT("OPENGL_WINDOW"),
                                        TEXT("OpenGL window"),
                                        WS_OVERLAPPEDWINDOW,
                                        0, 0,
                                        800, 600,
                                        NULL,
                                        NULL,
                                        instance_handle,
                                        NULL);
        
    dc = GetDC(window_handle);
        
    ShowWindow(window_handle, SW_SHOW);
    /* *************** */

    /* NEW CONTEXT */
    GLint context_attributes[] = {
        WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
        WGL_CONTEXT_MINOR_VERSION_ARB, 3,
        WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
        0
    };

    rc = wglCreateContextAttribsARB(dc, 0, context_attributes);
    wglMakeCurrent(dc, rc);
    /* *********** */
        
    /* EVENT PUMP */
    MSG msg;
        
    while (true) {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT) 
                break;
                
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
            
        // draw(); <- there goes your drawing
            
        SwapBuffers(dc);
    }
    /* ********** */
        
    return 0;
}

// Procedure that processes window events
LRESULT CALLBACK window_procedure(HWND window_handle, UINT message, WPARAM param_w, LPARAM param_l)
{
    /* When destroying the dummy window, WM_DESTROY message is going to be sent,
       but we don't want to quit the application then, and that is controlled by
       the quit flag. */

    switch(message) {
    case WM_DESTROY:
        if (!quit) quit = true;
        else PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(window_handle, message, param_w, param_l);
}

/* A procedure for getting OpenGL functions and OpenGL or WGL extensions.
   When looking for OpenGL 1.2 and above, or extensions, it uses wglGetProcAddress,
   otherwise it falls back to GetProcAddress. */
void* get_proc(const char *proc_name)
{
    void *proc = (void*)wglGetProcAddress(proc_name);
    if (!proc) proc = (void*)GetProcAddress(gl_module, proc_name);

    return proc;
}

Kompilerad med g++ GLExample.cpp -lopengl32 -lgdi32 med MinGW / Cygwin eller cl GLExample.cpp opengl32.lib gdi32.lib user32.lib med MSVC-kompilator. Se dock till att rubrikerna från OpenGL-registret finns i inkluderingsvägen. Om inte, använd -I flagga för g++ eller /I för cl för att berätta kompilatorn var de är.

Skapa OpenGL 4.1 med C ++ och Cocoa

Obs: Det kommer att finnas en del Objekt-c i det här exemplet .. Vi kommer att göra en omslag till C ++ i detta exempel, så oroa dig inte så mycket om det.

Starta först Xcode och skapa ett projekt.

ange bildbeskrivning här

Och välj ett Cocoa-program ange bildbeskrivning här

Ta bort alla källor utom Info.plist-filen. (Din app fungerar inte utan den)

Skapa 4 nya källfiler: En Objekt-c ++ -fil och rubrik (jag har kallat min MacApp) En klass C ++ (jag har kallat min (Application)

Klicka på det längst upp till vänster (med projektnamnet) och lägg till länkade ramverk och bibliotek. Lägg till: OpenGL.Framework AppKit.Framework GLKit.Framework

Ditt projekt kommer förmodligen att se ut så här:

ange bildbeskrivning här

NSApplication är den huvudklass du använder när du skapar en MacOS-app. Det låter dig registrera fönster och fånga händelser.

Vi vill registrera (vårt eget) fönster till NSA-ansökan. Skapa först i ditt objektiv-c ++ rubrik en objektiv-c-klass som ärver från NSWindow och implementerar NSApplicationDelegate NSWindow behöver en pekare till C ++ -programmet, en openGL-vy och en timer för dragslingan

//Mac_App_H
#import <Cocoa/Cocoa.h>
#import "Application.hpp"
#import <memory>
NSApplication* application;

@interface MacApp : NSWindow <NSApplicationDelegate>{
    std::shared_ptr<Application> appInstance;
}
@property (nonatomic, retain) NSOpenGLView* glView;
-(void) drawLoop:(NSTimer*) timer;
@end

Vi kallar detta från huvudsakligen med

int main(int argc, const char * argv[]) {
    MacApp* app;
    application = [NSApplication sharedApplication];
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 
    //create a window with the size of 600 by 600   
    app = [[MacApp alloc] initWithContentRect:NSMakeRect(0, 0, 600, 600)              styleMask:NSTitledWindowMask | NSClosableWindowMask |  NSMiniaturizableWindowMask   backing:NSBackingStoreBuffered defer:YES];    
    [application setDelegate:app];
    [application run];
}

Implementeringen av vårt fönster är faktiskt ganska enkelt. Först förklarar vi med syntese vår glview och lägger till ett globalt objektiv-coolean när fönstret ska stängas.

#import "MacApp.h"

@implementation MacApp

@synthesize glView;

BOOL shouldStop = NO;

Nu för konstruktören. Jag föredrar att använda initWithContentRect.

-(id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag{
if(self = [super initWithContentRect:contentRect styleMask:aStyle backing:bufferingType defer:flag]){
    //sets the title of the window (Declared in Plist)
    [self setTitle:[[NSProcessInfo processInfo] processName]];
 
    //This is pretty important.. OS X starts always with a context that only supports openGL 2.1
    //This will ditch the classic OpenGL and initialises openGL 4.1
    NSOpenGLPixelFormatAttribute pixelFormatAttributes[] ={
        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
            NSOpenGLPFAColorSize    , 24                           ,
            NSOpenGLPFAAlphaSize    , 8                            ,
            NSOpenGLPFADoubleBuffer ,
            NSOpenGLPFAAccelerated  ,
            NSOpenGLPFANoRecovery   ,
            0
    };

    NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc]initWithAttributes:pixelFormatAttributes];
    //Initialize the view 
    glView = [[NSOpenGLView alloc]initWithFrame:contentRect pixelFormat:format];
    
    //Set context and attach it to the window
    [[glView openGLContext]makeCurrentContext];
  
    //finishing off
    [self setContentView:glView];
    [glView prepareOpenGL];
    [self makeKeyAndOrderFront:self];
    [self setAcceptsMouseMovedEvents:YES];
    [self makeKeyWindow];
    [self setOpaque:YES];

    //Start the c++ code
    appInstance = std::shared_ptr<Application>(new Application());

}
return self;
}

Okej ... nu har vi faktiskt en körbar app ... Du kan se en svart skärm eller flimra.

Låt oss börja rita en fantastisk triangel. (I c ++)

Min applikationsrubrik

#ifndef Application_hpp
#define Application_hpp
#include <iostream>
#include <OpenGL/gl3.h>
class Application{
private:
    GLuint          program;
    GLuint          vao;
public:
    Application();
    void update();
    ~Application();

};

#endif /* Application_hpp */

Genomförandet:

Application::Application(){
 static const char * vs_source[] =
    {
        "#version 410 core                                                 \n"
        "                                                                  \n"
        "void main(void)                                                   \n"
        "{                                                                 \n"
        "    const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0),  \n"
        "                                   vec4(-0.25, -0.25, 0.5, 1.0),  \n"
        "                                   vec4( 0.25,  0.25, 0.5, 1.0)); \n"
        "                                                                  \n"
        "    gl_Position = vertices[gl_VertexID];                          \n"
        "}                                                                 \n"
    };

    static const char * fs_source[] =
    {
        "#version 410 core                                                 \n"
        "                                                                  \n"
        "out vec4 color;                                                   \n"
        "                                                                  \n"
        "void main(void)                                                   \n"
        "{                                                                 \n"
        "    color = vec4(0.0, 0.8, 1.0, 1.0);                             \n"
        "}                                                                 \n"
    };

    program = glCreateProgram();
    GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fs, 1, fs_source, NULL);
    glCompileShader(fs);

    GLuint vs = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vs, 1, vs_source, NULL);
    glCompileShader(vs);

    glAttachShader(program, vs);
    glAttachShader(program, fs);

    glLinkProgram(program);

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
}

void Application::update(){
    static const GLfloat green[] = { 0.0f, 0.25f, 0.0f, 1.0f };
    glClearBufferfv(GL_COLOR, 0, green);

    glUseProgram(program);
    glDrawArrays(GL_TRIANGLES, 0, 3);
}


Application::~Application(){
    glDeleteVertexArrays(1, &vao);
    glDeleteProgram(program);
}

Nu behöver vi bara ringa uppdatering om och om igen (om du vill att något ska flytta) Implementera i din objektiv-c-klass

-(void) drawLoop:(NSTimer*) timer{

if(shouldStop){
    [self close];
    return;
}
if([self isVisible]){
  
       appInstance->update();
    [glView update];
    [[glView openGLContext] flushBuffer];
}

}

Och lägg till den här metoden i implementeringen av din objektiv-c-klass:

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
    [NSTimer scheduledTimerWithTimeInterval:0.000001 target:self selector:@selector(drawLoop:) userInfo:nil repeats:YES];
}

detta kommer att ringa uppdateringsfunktionen för din c ++ klass om och om igen (varje 0.000001 sekunder för att vara exakt)

För att avsluta stänger vi fönstret när du trycker på stängningsknappen:

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication     *)theApplication{
    return YES;
}

- (void)applicationWillTerminate:(NSNotification *)aNotification{
    shouldStop = YES;
}

Grattis, nu har du ett fantastiskt fönster med en OpenGL-triangel utan några ramar från tredje part. slutresultat

Övergripande plattform för OpenGL-kontext (med SDL2)

Skapa ett fönster med OpenGL-kontext (tilläggsbelastning via GLEW ):

#define GLEW_STATIC

#include <GL/glew.h>
#include <SDL2/SDL.h>

int main(int argc, char* argv[])
{
    SDL_Init(SDL_INIT_VIDEO); /* Initialises Video Subsystem in SDL */

    /* Setting up OpenGL version and profile details for context creation */
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
    
    /* A 800x600 window. Pretty! */
    SDL_Window* window = SDL_CreateWindow
        (
        "SDL Context",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        800, 600,
        SDL_WINDOW_OPENGL
        );
    
    /* Creating OpenGL Context */
    SDL_GLContext gl_context = SDL_GL_CreateContext(window);

    /* Loading Extensions */
    glewExperimental = GL_TRUE;
    glewInit();

    /* The following code is for error checking. 
    *  If OpenGL has initialised properly, this should print 1.
    *  Remove it in production code.
    */
    GLuint vertex_buffer;
    glGenBuffers(1, &vertex_buffer);
    printf("%u\n", vertex_buffer);
    /* Error checking ends here */

    /* Main Loop */
    SDL_Event window_event;
    while(1) {
        if (SDL_PollEvent(&window_event)) {
            if (window_event.type == SDL_QUIT) {
                /* If user is exiting the application */
                break;
            }
        }
        /* Swap the front and back buffer for flicker-free rendering */
        SDL_GL_SwapWindow(window);
    }
    
    /* Freeing Memory */
    glDeleteBuffers(1, &vertex_buffer);
    SDL_GL_DeleteContext(gl_context);
    SDL_Quit();

    return 0;
}

Installera Modern OpenGL 4.1 på macOS (Xcode, GLFW och GLEW)

1. Installera GLFW

Det första steget är att skapa ett OpenGL-fönster. GLFW är ett Open Source, multi-platform bibliotek för att skapa fönster med OpenGL, för att installera GLFW först ladda ner sina filer från www.glfw.org

GLFW-webbsida

Extrahera GLFW-mappen så kommer dess innehåll att se ut så här

GLFW-mappinnehåll

Ladda ner och installera CMake för att bygga GLFW. Gå till www.cmake.org/download/ , ladda ner CMake och installera för MAC OS X

CMake Nedladdningar Webbsida

Om Xcode inte är installerat. Ladda ner och installera Xcode från Mac App Store.

Xcode från Mac App Store

Skapa en ny mapp Bygg in i GLFW-mappen

GLFW-mapp efter att ha skapat "Build" -mappen

Öppna CMake, klicka på knappen Browse Source för att välja GLFW-mappen (se till att CMakeLists.txt) finns i den mappen. Därefter klickar du på Bläddra Build- knappen och väljer den nyskapade Build- mappen i föregående steg.

CMake-vägar

Klicka nu på Konfigurera- knappen och välj Xcode som generator med alternativet Alternativa ursprungliga kompilatorer och klicka på Klar .

Makefile för Xcode

Markera alternativet BUILD_SHARED_LIBS och klicka sedan på Konfigurera- knappen igen och klicka slutligen på Generera- knappen.

Välj BUILD_SHARED_LIBS

Efter generationen bör CMake se ut så här

Sista CMake

Öppna nu Finder och gå till / usr , skapa ett mappnamn lokalt om det inte redan finns. Öppna den lokala mappen och skapa två mappar inklusive och lib om de inte redan finns.

Öppna nu GLFW-mappen och gå till Build (där CMake hade byggt filerna). Öppna filen GLFW.xcodeproj i Xcode.

Xcode-projektfil

Välj installera> Min Mac och klicka sedan på kör (Spela formad knapp).

Installera GLFW

Det har nu installerats med framgång (ignorera varningarna).

För att se till att Open Finder och goto / usr / local / lib- mappen och tre GLFW-biblioteksfiler redan finns där (Om inte, öppna sedan Build- mappen i GLFW-mappen och gå till src / Debug copy alla filer till / usr / local / lib )

GLFW Lib-filer

Open Finder och goto / usr / local / include och en GLFW-mapp kommer redan att finnas där med två rubrikfiler inuti den med namnet glfw3.h och glfw3native.h

GLFW Header Files

2. Installera GLEW

GLEW är ett bibliotek över plattformar som hjälper till att fråga och ladda OpenGL-tillägg. Det tillhandahåller körtidsmekanismer för att bestämma vilka OpenGL-tillägg som stöds på målplattformen. Det är bara för modern OpenGL (OpenGL version 3.2 och högre som kräver att funktioner fastställs vid körning). För att installera först ladda ner dess filer från glew.sourceforge.net

GLEW-webbsida

Extrahera GLFW-mappen så kommer dess innehåll att se ut så här.

GLEW-mappinnehåll

Öppna nu Terminal, navigera till GLEW-mapp och skriv följande kommandon

make
sudo make install 
make clean

Nu är GLEW installerat. För att se till att det är installerat, Open Finder, gå till / usr / local / include och en GL-mapp kommer redan att finnas där med tre sidhuvudfiler inuti den med namnet glew.h , glxew.h och wglew.h

GLEW Header Files

Öppna Finder och gå till / usr / local / lib och GLEW-biblioteksfiler kommer redan att finnas där

GLEW-biblioteksfiler

3. Testa och kör

Nu har vi framgångsrikt installerat GLFW och GLEW. Det är dags att koda. Öppna Xcode och skapa ett nytt Xcode-projekt. Välj kommandoradsverktyg och fortsätt sedan nästa och välj C ++ som språk.

Xcode-projekt

Xcode skapar ett nytt kommandoradsprojekt.

Klicka på projektnamnet, och under Build fliken Inställningar växla från Basic till All under sökvägar avsnittet lägga till / usr / local / inkludera i Header sökvägar och lägga till / usr / local / lib i biblioteket sökvägar

Sökvägar

Klicka på projektnamn, och under fliken Byggfaser och under Länk med binära bibliotek lägg till OpenGL.framework och lägg till nyligen skapade GLFW- och GLEW- bibliotek från / usr / local / lib

Länkbinarier

Nu är vi redo att koda i Modern Open GL 4.1 på macOS med C ++ och Xcode. Följande kod skapar ett OpenGL-fönster med GLFW med Blank Screen Output.

#include <GL/glew.h> 
#include <GLFW/glfw3.h>

// Define main function
int main() 
{
    // Initialize GLFW
    glfwInit();

    // Define version and compatibility settings
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 
    glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create OpenGL window and context
    GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", NULL, NULL);
    glfwMakeContextCurrent(window);

    // Check for window creation failure
    if (!window) 
    {
        // Terminate GLFW
        glfwTerminate();
        return 0; 
    }

    // Initialize GLEW
    glewExperimental = GL_TRUE; glewInit();

    // Event loop
    while(!glfwWindowShouldClose(window)) 
    {
        // Clear the screen to black
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT);
        glfwSwapBuffers(window);
        glfwPollEvents(); 
    }

    // Terminate GLFW
    glfwTerminate(); return 0;
}

Tomt OpenGL-fönster

Skapa Opengl-kontext med Java och LWJGL 3.0

I det här exemplet kommer vi att skapa ett tomt Opengl-fönster med LWJGL 3.0+, detta innehåller inte steg för att skapa projektet i din IDE

ange bildbeskrivning här

  1. Skapa ett klassnamn WindowManager som kommer att innehålla all pannplåtkod för att skapa ett opengl-kontextfönster på skärmen

WindowManager.java

import org.lwjgl.glfw.*;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;

/**
 * Class Containing code related to inflating Opengl Window
 */
public class Displaymanager {

    private static long window;

    public static void createDisplay(){
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        GLFWErrorCallback.createPrint(System.err).set();

        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if ( !glfwInit() )
            throw new IllegalStateException("Unable to initialize GLFW");

        // Configure our window
        glfwDefaultWindowHints(); // optional, the current window hints are already the default
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable

        int WIDTH = 300;
        int HEIGHT = 300;

        // Create the window
        window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World!", NULL, NULL);
        if ( window == NULL )
            throw new RuntimeException("Failed to create the GLFW window");

        // Setup a key callback. It will be called every time a key is pressed, repeated or released.
        glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
            if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
                glfwSetWindowShouldClose(window, true); // We will detect this in our rendering loop
        });

        // Get the resolution of the primary monitor
        GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        // Center our window
        glfwSetWindowPos(
                window,
                (vidmode.width() - WIDTH) / 2,
                (vidmode.height() - HEIGHT) / 2
        );

        // Make the OpenGL context current
        glfwMakeContextCurrent(window);
        // Enable v-sync
        glfwSwapInterval(1);

        // Make the window visible
        glfwShowWindow(window);
    }

    public static boolean isCloseRequested(){
        return glfwWindowShouldClose(window);
    }

    public static void updateDisplay(){
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer

        glfwSwapBuffers(window); // swap the color buffers

        // Poll for window events. The key callback above will only be
        // invoked during this call.
        glfwPollEvents();
    }

    public static void destroyDisplay(){
        // Terminate GLFW and free the error callback
        cleanUp();
        glfwTerminate();
        glfwSetErrorCallback(null).free();
    }

    private static void cleanUp() {
        // Free the window callbacks and destroy the window
        glfwFreeCallbacks(window);
        glfwDestroyWindow(window);
    }
}
  1. Skapa sedan en klass som innehåller huvud rendering loop, som kommer att kalla alla ovanstående funktion skapad

OpenGlMain.java

import org.lwjgl.opengl.GL;
import renderEngine.Displaymanager;
import static org.lwjgl.opengl.GL11.glClearColor;


/**
 * Class to test the opengl Window
 */
public class OpenGlMain {

    public static void main(String[] args) {

        Displaymanager.createDisplay();

        // This line is critical for LWJGL's interoperation with GLFW's
        // OpenGL context, or any context that is managed externally.
        // LWJGL detects the context that is current in the current thread,
        // creates the GLCapabilities instance and makes the OpenGL
        // bindings available for use.
        GL.createCapabilities();

        while (!Displaymanager.isCloseRequested()){

            // Set the clear color
            glClearColor(1.0f, 0.0f, 0.0f, 0.0f);

            Displaymanager.updateDisplay();
        }

        Displaymanager.destroyDisplay();
    }
}

För mer information om kassans officiella LWJGL-guide



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow