OpenGL is an open standard for rendering 2D and 3D graphics leveraging graphics hardware. OpenGL has been implemented across a stunning array of platforms allowing apps targeting OpenGL to be extremely flexible.
Version | Release Date |
---|---|
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 |
In this example code we will create A blank Opengl Window using LWJGL 3.0+, this doesn't contain steps to create the project in your IDE
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);
}
}
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();
}
}
For further detail checkout official LWJGL Guide
Note: There will be some Objective-c in this example.. We will make a wrapper to C++ in this example, So don't worry to much about it.
First start Xcode and create a project.
And select a Cocoa application
Delete all sources except the Info.plist file.(Your app won't work without it)
Create 4 new source-files: A Objective-c++ file and header (I've called mine MacApp) A C++ class (I've called mine (Application)
In the top left (with the project name) click on it and add linked frameworks and libraries. Add: OpenGL.Framework AppKit.Framework GLKit.Framework
Your project will look probably like this:
NSApplication is the main class you use while creating a MacOS app. It allows you to register windows and catch events.
We want to register (our own) window to the NSApplication. First create in your objective-c++ header a objective-c class that inherits from NSWindow and implements NSApplicationDelegate The NSWindow needs a pointer to the C++ application, A openGL View and a timer for the draw loop
//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
We call this from the main with
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];
}
The implementation of our window is actually quite easy First we declare with synthesise our glview and add a global objective-c boolean when the window should close.
#import "MacApp.h"
@implementation MacApp
@synthesize glView;
BOOL shouldStop = NO;
Now for the constructor. My preference is to use the 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;
}
Alright... now we have actually a runnable app.. You might see a black screen or flickering.
Let's start drawing a awesome triangle.(in c++)
My application header
#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 */
The implementation:
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);
}
Now we only need to call update over and over again(if you want something to move) Implement in your objective-c class
-(void) drawLoop:(NSTimer*) timer{
if(shouldStop){
[self close];
return;
}
if([self isVisible]){
appInstance->update();
[glView update];
[[glView openGLContext] flushBuffer];
}
}
And add the this method in the implementation of your objective-c class:
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[NSTimer scheduledTimerWithTimeInterval:0.000001 target:self selector:@selector(drawLoop:) userInfo:nil repeats:YES];
}
this will call the update function of your c++ class over and over again(each 0.000001 seconds to be precise)
To finish up we close the window when the close button is pressed:
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication{
return YES;
}
- (void)applicationWillTerminate:(NSNotification *)aNotification{
shouldStop = YES;
}
Congratulations, now you have a awesome window with a OpenGL triangle without any third party frameworks.
Creating a Window with OpenGL context (extension loading through 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; }
Full example code included at the end
WGL (can be pronounced wiggle) stands for "Windows-GL", as in "an interface between Windows and OpenGL" - a set of functions from the Windows API to communicate with OpenGL. WGL functions have a wgl prefix and its tokens have a WGL_ prefix.
Default OpenGL version supported on Microsoft systems is 1.1. That is a very old version (most recent one is 4.5). The way to get the most recent versions is to update your graphics drivers, but your graphics card must support those new versions.
Full list of WGL functions can be found here.
GDI (today updated to GDI+) is a 2D drawing interface that allows you to draw onto a window in Windows. You need GDI to initialize OpenGL and allow it to interact with it (but will not actually use GDI itself).
In GDI, each window has a device context (DC) that is used to identify the drawing target when calling functions (you pass it as a parameter). However, OpenGL uses its own rendering context (RC). So, DC will be used to create RC.
So for doing things in OpenGL, we need RC, and to get RC, we need DC, and to get DC we need a window. Creating a window using the Windows API requires several steps. This is a basic routine, so for a more detailed explanation, you should consult other documentation, because this is not about using the Windows API.
This is a Windows setup, so Windows.h
must be included, and the entry point of the program must be WinMain
procedure with its parameters. The program also needs to be linked to opengl32.dll
and to gdi32.dll
(regardless of whether you are on 64 or 32 bit system).
First we need to describe our window using the WNDCLASS
structure. It contains information about the window we want to create:
/* 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);
/* *************** */
For a precise explanation of the meaning of each field (and for a full list of fields), consult MSDN documenation.
Then, we can create a window using CreateWindowEx
. After the window is created, we can acquire its DC:
/* 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);
/* ************* */
Finally, we need to create a message loop that receives window events from the 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
}
/* ********** */
OpenGL needs to know some information about our window, such as color bitness, buffering method, and so on. For this, we use a pixel format. However, we can only suggest to the OS what kind of a pixel format we need, and the OS will supply the most similar supported one, we don't have direct control over it. That is why it is only called a descriptor.
/* 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);
/* *********************** */
We've enabled double buffering in the dwFlags
field, so we must call SwapBuffers
in order to see things after drawing.
After that, we can simply create our rendering context:
/* RENDERING CONTEXT */
HGLRC rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
/* ***************** */
Note that only one thread can use the RC at a time. If you wish to use it from another thread later, you must call wglMakeCurrent
there to activate it again (this will deactivate it on the thread it's currently active, and so on).
OpenGL functions are obtained by using function pointers. The general procedure is:
For example, consider 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" means "pointer to function", then follows the name of an OpenGL function, and "PROC" at the end - that is the usual OpenGL function pointer type name.)
Here's how it's done on Windows. As mentioned previously, Microsoft only ships OpenGL 1.1. First, function pointer types for that version can be found by including GL/gl.h
. After that, we declare all the functions we intend to use as shown above (doing that in a header file and declaring them "extern" would allow us to use them all after loading them once, just by including it). Finally, loading the OpenGL 1.1 functions is done by opening the DLL:
HMODULE gl_module = LoadLibrary(TEXT("opengl32.dll"));
/* Load all the functions here */
glBegin = (PFNGLBEGINPROC)GetProcAddress("glBegin");
// ...
/* *************************** */
FreeLibrary(gl_module);
However, we probably want a little bit more than OpenGL 1.1. But Windows doesn't give us the function prototypes or exported functions for anything above that. The prototypes need to be acquired from the OpenGL registry. There are three files of interest to us: GL/glext.h
, GL/glcorearb.h
, and GL/wglext.h
.
In order to complete GL/gl.h
provided by Windows, we need GL/glext.h
. It contains (as described by the registry) "OpenGL 1.2 and above compatibility profile and extension interfaces" (more about profiles and extensions later, where we'll see that it's actually not a good idea to use those two files).
The actual functions need to be obtained by wglGetProcAddress
(no need for opening the DLL for this guy, they aren't in there, just use the function). With it, we can fetch all the functions from OpenGL 1.2 and above (but not 1.1). Note that, in order for it to function properly, the OpenGL rendering context must be created and made current. So, for example, 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");
We can actually build a wrapper get_proc
procedure that uses both wglGetProcAddress
and 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;
}
So to wrap up, we would create a header file full of function pointer declarations like this:
extern PFNGLCLEARCOLORPROC glClearColor;
extern PFNGLCLEARDEPTHPROC glClearDepth;
extern PFNGLCLEARPROC glClear;
extern PFNGLCLEARBUFFERIVPROC glClearBufferiv;
extern PFNGLCLEARBUFFERFVPROC glClearBufferfv;
// And so on...
We can then create a procedure like load_gl_functions
that we call only once, and works like so:
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");
And you're all set! Just include the header with the function pointers and GL away.
OpenGL has been in development for over 20 years, and the developers were always strict about backwards compatibility (BC). Adding a new feature is very hard because of that. Thus, in 2008, it was separated into two "profiles". Core and compatibility. Core profile breaks BC in favor of performance improvements and some of the new features. It even completely removes some legacy features. Compatibility profile maintains BC with all versions down to 1.0, and some new features are not available on it. It is only to be used for old, legacy systems, all new applications should use the core profile.
Because of that, there is a problem with our basic setup - it only provides the context that is backwards compatible with OpenGL 1.0. The pixel format is limited too. There is a better approach, using extensions.
Any addition to the original functionality of OpenGL are called extensions. Generally, they can either make some things legal that weren't before, extend parameter value range, extend GLSL, and even add completely new functionality.
There are three major groups of extensions: vendor, EXT, and ARB. Vendor extensions come from a specific vendor, and they have a vendor specific mark, like AMD or NV. EXT extensions are made by several vendors working together. After some time, they may become ARB extensions, which are all the officially supported ones and ones approved by ARB.
To acquire function pointer types and function prototypes of all the extensions and as mentioned before, all the function pointer types from OpenGL 1.2 and greater, one must download the header files from the OpenGL registry. As discussed, for new applications it's better to use core profile, so it would be preferrable to include GL/glcorearb.h
instead of GL/gl.h
and GL/glext.h
(if you are using GL/glcorearb.h
then don't include GL/gl.h
).
There are also extensions for the WGL, in GL/wglext.h
. For example, the function for getting the list of all supported extensions is actually an extension itself, the wglGetExtensionsStringARB
(it returns a big string with a space-separated list of all the supported extensions).
Getting extensions is handled via wglGetProcAddress
too, so we can just use our wrapper like before.
The WGL_ARB_pixel_format
extension allows us the advanced pixel format creation. Unlike before, we don't use a struct. Instead, we pass the list of wanted attributes.
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);
Similarly, the WGL_ARB_create_context
extension allows us the advanced context creation:
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);
For a precise explanation of the parameters and functions, consult the OpenGL specification.
Why didn't we just start off with them? Well, that's because the extensions allow us to do this, and to get extensions we need wglGetProcAddress
, but that only works with an active valid context. So in essence, before we are able to create the context we want, we need to have some context active already, and it's usually referred to as a dummy context.
However, Windows doesn't allow setting the pixel format of a window more than once. Because of that, the window needs to be destroyed and recreated in order to apply new things:
wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
ReleaseDC(window_handle, dc);
DestroyWindow(window_handle);
// Recreate the window...
Full example code:
/* 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;
}
Compiled with g++ GLExample.cpp -lopengl32 -lgdi32
with MinGW/Cygwin or cl GLExample.cpp opengl32.lib gdi32.lib user32.lib
with MSVC compiler. Make sure however, that the headers from the OpenGL registry are in the include path. If not, use -I
flag for g++
or /I
for cl
in order to tell the compiler where they are.
One of the most common misconceptions about OpenGL is, that it were a library that could be installed from 3rd party sources. This misconception leads to many questions in the form "how to I install OpenGL" or "where to download the OpenGL SDK".
This is not how OpenGL finds the way into computer system. OpenGL by itself is merely a set of specifications on what commands an implementation must follow. So it's the implementation that matters. And for the time being, OpenGL implementations are part of the GPU drivers. This might change in the future, when new GPU programming interface allow to truly implement OpenGL as a library, but for now it's a programming API towards the graphics drivers.
When OpenGL got first released the API somehow found its way into the ABI (Application Binary Interface) contract of Windows, Solaris and Linux (LSB-4 Desktop) in addition to it's origin Sun Irix. Apple followed and in fact integrated OpenGL so deep into MacOS X, that the OpenGL version available is tightly coupled to the version of MacOS X installed. This has the notable effect, that system programming environments for these operating systems (i.e. the compiler and linker toolchain that natively targets these systems) must deliver also OpenGL API definitions. Such it is not necessary to actually install an SDK for OpenGL. It is technically possible to program OpenGL on these operating systems without the requirement to install a dedicated SDK, assuming that a build environment following the targeted ABI is installed.
A side effect of these strict ABI rules is, that the OpenGL version exposed through the binding interface is a lowest common denominator that programs running on the target platform may expect to be available. Hence modern OpenGL features are to be accessed through the extension mechanism, which is described in depth separately.
In Linux it is quite common to compartmentize the development packages for different aspects of the system, so that these can be updated individually. In most Linux distributions the development files for OpenGL are contained in a dedicated package, that is usually a dependency for a desktop application development meta-package. So installing the OpenGL development files for Linux is usually taken care of with the installation of the desktop development meta package/s.*
The API binding library opengl32.dll
(named so for both 32 bit and 64 bit versions of Windows) is shipped by default with every Windows version since Windows NT-4 and Windows 95B (both ca. 1997). However this DLL does not provide an actual OpenGL implementation (apart from a software fallback which sole purpose is to act as a safety net for programs if no other OpenGL implementation is installed). This DLL belongs to Windows and must not be altered or moved! Modern OpenGL versions are shipped as part of the so called Installable Client Driver (ICD) and accessed through the default opengl32.dll
that comes pre-installed with every version of Windows. It was decided internally by Microsoft, however, that graphics drivers installed through Windows Update would not install/update a OpenGL ICD. As such fresh installations of Windows with drivers installed automatically are lacking support for modern OpenGL features. To obtain an OpenGL ICD with modern features, graphics drivers must be downloaded directly from the GPU vendor's website and installed manually.
Regarding development no extra steps must be taken per-se. All C/C++ compilers following the Windows ABI specifications ship with headers and the linker stub (opengl32.lib) required to build and link executables that make use of OpenGL.
1. Install GLFW
First step is to create an OpenGL window. GLFW is an Open Source, multi-platform library for creating windows with OpenGL, to install GLFW first download its files from www.glfw.org
Extract the GLFW folder and its contents will look like this
Download and install CMake to build GLFW. Goto www.cmake.org/download/, download CMake and install for MAC OS X
If Xcode is not installed. Download and install Xcode from Mac App Store.
Create a new folder Build inside the GLFW folder
Open CMake, click on Browse Source button to select the GLFW folder (make sure that CMakeLists.txt) is located inside that folder. After that, click on Browse Build button and select the newly created Build folder in previous step.
Now Click on Configure button and select Xcode as generator with Use default native compilers option, and click Done.
Tick on BUILD_SHARED_LIBS option and then click on Configure button again and finally click Generate button.
After generation CMake should look like this
Now Open Finder and goto /usr, create a folder name local if not already there. Open the local folder and create two folders include and lib if not already there.
Now open the GLFW folder and goto Build (where CMake had built the files). Open GLFW.xcodeproj file in Xcode.
Select install > My Mac and then click on run (Play shaped button).
It is now successfully installed (ignore the warnings).
To make sure Open Finder and goto /usr/local/lib folder and three GLFW library files will be already present there (If not then open Build folder inside GLFW folder and go to src/Debug copy all files to /usr/local/lib)
Open Finder and goto /usr/local/include and a GLFW folder will be already present there with two header files inside it by name of glfw3.h and glfw3native.h
2. Install GLEW
GLEW is a cross-platform library that helps in querying and loading OpenGL extensions. It provides run-time mechanisms for determining which OpenGL extensions are supported on the target platform. It is only for modern OpenGL (OpenGL version 3.2 and greater which requires functions to be determined at runtime). To install first download its files from glew.sourceforge.net
Extract the GLFW folder and its contents will look like this.
Now open Terminal, navigate to GLEW Folder and type the following commands
make
sudo make install
make clean
Now GLEW is successfully installed. To make sure its installed, Open Finder, go to /usr/local/include and a GL folder will be already present there with three header files inside it by name of glew.h, glxew.h and wglew.h
Open Finder and go to /usr/local/lib and GLEW library files will be already present there
3. Test and Run
Now we have successfully installed GLFW and GLEW. Its time to code. Open Xcode and create a new Xcode project. Select Command Line Tool then proceed next and select C++ as language.
Xcode will create a new command line project.
Click on project name, and under Build Settings tab switch from Basic to All, under Search Paths section, add /usr/local/include in Header Search Paths and add /usr/local/lib in Library Search Paths
Click on project name, and under Build Phases tab and under Link With Binary Libraries add OpenGL.framework and also add recently created GLFW and GLEW libraries from /usr/local/lib
Now we are ready to code in Modern Open GL 4.1 on macOS using C++ and Xcode. The following code will create an OpenGL Window using GLFW with 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;
}