opengl开始使用opengl


备注

OpenGL是一种开放标准,用于渲染利用图形硬件的2D和3D图形。 OpenGL已经在一系列令人惊叹的平台上实现,允许针对OpenGL的应用程序非常灵活。

版本

发布日期
1.1 1997年3月4日
1.2 1998年3月16日
1.2.1 1998年10月14日
1.3 2001年8月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 2010-03-11
4.1 2010-07-26
4.2 2011-08-08
4.3 2012-08-06
4.4 2013年7月22日
4.5 2014年8月11日

使用Java和LWJGL 3.0创建Opengl Context

在这个示例代码中,我们将使用LWJGL 3.0+创建一个空白的Opengl窗口,这不包含在IDE中创建项目的步骤

在此处输入图像描述

  1. 创建一个类名WindowManager,它将包含用于在屏幕上创建opengl上下文窗口的所有样板代码

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. 接下来创建一个包含主渲染循环的类,它将调用所有上面创建的函数

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

有关详细信息,请查看官方LWJGL指南

使用C ++和Cocoa创建OpenGL 4.1

注意:在这个例子中会有一些Objective-c。在这个例子中我们将为C ++做一个包装器,所以不用担心它。

首先启动Xcode并创建一个项目。

在此处输入图像描述

并选择一个Cocoa应用程序在此处输入图像描述

删除除Info.plist文件以外的所有来源。(如果没有它,您的应用程序将无法运行)

创建4个新的源文件:Objective-c ++文件和标题(我称之为我的MacApp)一个C ++类(我称之为我的(应用程序)

在左上角(带有项目名称),单击它并添加链接的框架和库。添加:OpenGL.Framework AppKit.Framework GLKit.Framework

您的项目可能看起来像这样:

在此处输入图像描述

NSApplication是您在创建MacOS应用程序时使用的主要类。它允许您注册窗口和捕获事件。

我们想要将(我们自己的)窗口注册到NSApplication。首先在objective-c ++头文件中创建一个Objective-c类,它继承自NSWindow并实现NSApplicationDelegate NSWindow需要一个指向C ++应用程序的指针,一个openGL视图和一个用于绘制循环的计时器

//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
 

我们称之为主要的

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

我们的窗口的实现实际上非常简单首先我们声明合成我们的glview并在窗口关闭时添加一个全局的objective-c布尔值。

#import "MacApp.h"

@implementation MacApp

@synthesize glView;

BOOL shouldStop = NO;
 

现在为构造函数。我的偏好是使用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;
}
 

好吧......现在我们实际上有一个可运行的应用程序..你可能会看到黑屏或闪烁。

让我们开始绘制一个很棒的三角形。(在c ++中)

我的应用标题

#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 */
 

实施:

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

现在我们只需要反复调用更新(如果你想要移动的东西)在objective-c类中实现

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

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

}
 

并在您的objective-c类的实现中添加此方法:

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

这将一遍又一遍地调用你的c ++类的更新函数(准确地说每个0.000001秒)

为了完成,我们在按下关闭按钮时关闭窗口:

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

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

恭喜,现在你有一个很棒的窗口,有一个没有任何第三方框架的OpenGL三角形。 最后结果

跨平台OpenGL上下文创建(使用SDL2)

使用OpenGL上下文创建窗口(通过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;
}

在Windows上手动设置OpenGL

最后包含完整的示例代码

OpenGL的Windows组件

WGL

WGL(可以发音为wiggle )代表“Windows-GL”,如“Windows和OpenGL之间的接口” - 来自Windows API的一组函数,用于与OpenGL通信。 WGL函数具有wgl前缀,其标记具有WGL_前缀。

Microsoft系统支持的默认OpenGL版本为1.1。这是一个非常旧的版本(最近的版本是4.5)。获取最新版本的方法是更新图形驱动程序,但您的图形卡必须支持这些新版本。

可以在此处找到WGL功能的完整列表。

图形设备接口(GDI)

GDI(今天更新为GDI +)是一个2D绘图界面,允许您在Windows中绘制窗口。您需要GDI来初始化OpenGL并允许它与它交互(但实际上不会使用GDI本身)。

在GDI中,每个窗口都有一个设备上下文(DC) ,用于在调用函数时标识绘图目标(将其作为参数传递)。但是,OpenGL使用自己的渲染上下文(RC) 。因此,DC将用于创建RC。


基本设置

创建一个窗口

因此,对于OpenGL中的操作,我们需要RC,要获得RC,我们需要DC,要获得DC,我们需要一个窗口。使用Windows API创建窗口需要几个步骤。 这是一个基本例程,因此有关更详细的说明,您应该参考其他文档,因为这不是关于使用Windows API。

这是一个Windows设置,因此必须包含Windows.h ,并且程序的入口点必须是带有参数的WinMain 过程。该程序还需要链接到opengl32.dllgdi32.dll (无论您使用的是64位还是32位系统)。

首先,我们需要使用WNDCLASS 结构来描述我们的窗口。它包含有关我们要创建的窗口的信息:

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

有关每个字段含义的精确解释(以及完整的字段列表),请参阅MSDN文档。

然后,我们可以使用CreateWindowEx 创建一个窗口。创建窗口后,我们可以获得其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);
/* ************* */
 

最后,我们需要创建一个从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需要了解有关窗口的一些信息,例如颜色位,缓冲方法等。为此,我们使用像素格式 。但是,我们只能向操作系统建议我们需要什么样的像素格式,操作系统将提供最相似的支持 ,我们无法直接控制它。这就是为什么它只被称为描述符

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

我们在dwFlags 字段中启用了双缓冲,因此我们必须调用SwapBuffers 以便在绘制后查看内容。

渲染上下文

之后,我们可以简单地创建渲染上下文:

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

请注意,一次只能有一个线程使用RC。如果你希望稍后从另一个线程使用它,你必须wglMakeCurrent 那里调用wglMakeCurrent 来再次激活它(这将在它当前处于活动状态的线程上停用它,依此类推)。

获得OpenGL功能

通过使用函数指针获得OpenGL函数。一般程序是:

  1. 以某种方式获取函数指针类型(本质上是函数原型)
  2. 声明我们想要使用的每个函数(使用其函数指针类型)
  3. 获得实际功能

例如,考虑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”表示“指向函数的指针”,然后是OpenGL函数的名称,最后是“PROC” - 这是通常的OpenGL函数指针类型名称。)

这是在Windows上完成的。如前所述,微软只发布OpenGL 1.1。首先,可以通过包含GL/gl.h 找到该版本的函数指针类型。在那之后,我们声明我们打算使用的所有函数,如上所示(在头文件中执行它并将它们声明为“extern”将允许我们在加载它们之后使用它们,只需包含它)。最后,通过打开DLL来加载OpenGL 1.1函数:

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

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

FreeLibrary(gl_module);
 

但是,我们可能想要比OpenGL 1.1更多一点。但Windows并没有为我们提供函数原型或导出函数。原型需要从OpenGL注册表中获取。我们感兴趣的文件有三个: GL/glext.hGL/glcorearb.hGL/wglext.h

为了完成Windows提供的GL/gl.h ,我们需要GL/glext.h 。它包含(如注册表所述)“OpenGL 1.2及以上兼容性配置文件和扩展接口”(稍后将详细介绍配置文件和扩展,我们将看到使用这两个文件实际上并不是一个好主意 )。

实际的函数需要通过wglGetProcAddress 获得(不需要为这个人打开DLL,它们不在那里,只需使用该函数)。有了它,我们可以从OpenGL 1.2及更高版本(但不是1.1)获取所有功能。请注意,为了使其正常运行, 必须创建OpenGL渲染上下文并使其成为当前 。所以,例如, 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");
 

我们实际上可以构建一个使用wglGetProcAddressGetProcAddress 的包装器get_proc 过程:

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

所以要结束,我们将创建一个充满函数指针声明的头文件,如下所示:

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

然后我们可以创建一个像load_gl_functions 这样的程序,我们只调用一次,其工作原理如下:

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

你们都准备好了!只需包含带有函数指针和GL的标题。


更好的设置

OpenGL配置文件

OpenGL已经开发了20多年,开发人员始终严格遵守向后兼容性(BC) 。因此,添加新功能非常困难。因此,在2008年,它被分为两个“概况”。 核心兼容性 。核心配置文件打破了BC,有利于性能改进和一些新功能。它甚至完全删除了一些遗留功能。兼容性配置文件维护BC的所有版本低至1.0,并且其中没有一些新功能。它仅用于旧的遗留系统,所有新应用程序都应使用核心配置文件。

因此,我们的基本设置存在问题 - 它只提供向后兼容OpenGL 1.0的上下文。像素格式也是有限的。有一种更好的方法,使用扩展。

OpenGL扩展

对OpenGL的原始功能的任何添加都称为扩展。通常,它们可以使某些事情变得合法,扩展参数值范围,扩展GLSL,甚至添加全新的功能。

有三个主要的扩展组:供应商,EXT和ARB。供应商扩展来自特定供应商,它们具有供应商特定标记,如AMD或NV。 EXT扩展由多个供应商共同完成。一段时间后,它们可能会成为ARB扩展,这些都是官方支持的ARB和ARB批准的扩展。

要获取所有扩展的函数指针类型和函数原型, 并且如前所述,来自OpenGL 1.2和更高版本的所有函数指针类型 ,必须从OpenGL注册表下载头文件。如上所述,对于新应用程序,最好使用核心配置文件,因此最好包括GL/glcorearb.h 而不是GL/gl.hGL/glext.h (如果您使用的是GL/glcorearb.h 那么请不包括GL/gl.h )。

GL/wglext.h 中还有WGL的扩展。例如,获取所有支持的扩展列表的函数实际上是一个扩展本身,即wglGetExtensionsStringARB (它返回一个大字符串,其中包含所有支持的扩展的空格分隔列表)。

获取扩展也是通过wglGetProcAddress 处理的,所以我们可以像以前一样使用我们的包装器。

高级像素格式和上下文创建

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

类似地, WGL_ARB_create_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
};

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

有关参数和功能的精确说明,请参阅OpenGL规范。

我们为什么不从他们开始呢?好吧,那是因为扩展允许我们这样做,并且为了获得扩展,我们需要wglGetProcAddress ,但这只适用于活动的有效上下文。所以实质上,在我们能够创建我们想要的上下文之前,我们需要已经有一些上下文活动,并且它通常被称为虚拟上下文

但是,Windows不允许多次设置窗口的像素格式。因此,需要销毁窗口并重新创建窗口以应用新的东西:

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

// Recreate the window...
 

完整示例代码:

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

使用带有MinGW / Cygwin的g++ GLExample.cpp -lopengl32 -lgdi32 或带有MSVC编译器的cl GLExample.cpp opengl32.lib gdi32.lib user32.lib 编译。但请确保OpenGL注册表中的标头位于包含路径中。如果没有,请为g++ 使用-I 标志,或为cl 使用/I ,以告诉编译器它们在哪里。

获取OpenGL

关于OpenGL的一个最常见的误解是,它是一个可以从第三方来源安装的库。这种误解导致了“如何安装OpenGL”或“在何处下载OpenGL SDK”形式的许多问题。

这不是OpenGL如何进入计算机系统的方式。 OpenGL本身仅仅是一组关于实现必须遵循的命令的规范。所以重要的是实施。目前,OpenGL实现是GPU驱动程序的一部分。当新的GPU编程接口允许真正实现OpenGL作为库时,这可能会在未来发生变化,但是现在它是针对图形驱动程序的编程API。

当OpenGL首次发布时,除了源自Sun Irix之外,API还以某种方式进入了Windows,Solaris和Linux(LSB-4桌面)的ABI(应用程序二进制接口)合同。 Apple遵循并实际上将OpenGL集成到MacOS X中,以至于可用的OpenGL版本与安装的MacOS X版本紧密耦合。这具有显着的效果,这些操作系统的系统编程环境(即本机地以这些系统为目标的编译器和链接器工具链)也必须提供OpenGL API定义。这样就没有必要为OpenGL实际安装SDK。假设安装了目标ABI之后的构建环境,在技术上可以在这些操作系统上编程OpenGL而无需安装专用SDK。

这些严格的ABI规则的副作用是,通过绑定接口公开的OpenGL版本是目标平台上运行的程序可能预期可用的最低公分母。因此,现代OpenGL功能将通过扩展机制进行访问,该机制将分别进行深入介绍。

Linux的

在Linux中,为系统的不同方面划分开发包是很常见的,因此可以单独更新这些包。在大多数Linux发行版中,OpenGL的开发文件包含在专用包中,该包通常是桌面应用程序开发元包的依赖项。所以安装Linux的OpenGL开发文件通常需要安装桌面开发元软件包。*

微软Windows

默认情况下,自Windows NT-4和Windows 95B(均为1997年)以来,每个Windows版本都附带了API绑定库opengl32.dll (对于32位和64位版本的Windows都是如此)。但是,此DLL不提供实际的OpenGL实现(除了软件回退,其唯一目的是在没有安装其他OpenGL实现的情况下充当程序的安全网)。此DLL属于Windows, 不得更改或移动!现代OpenGL版本作为所谓的可安装客户端驱动程序 (ICD)的一部分提供,并通过每个Windows版本预安装的默认opengl32.dll 访问。然而,由微软内部决定,通过Windows Update安装的图形驱动程序不会安装/更新OpenGL ICD。因此,自动安装驱动程序的Windows的全新安装缺乏对现代OpenGL功能的支持。要获得具有现代功能的OpenGL ICD,必须直接从GPU供应商的网站下载图形驱动程序并手动安装。

关于开发,不必采取额外的步骤。遵循Windows ABI规范的所有C / C ++编译器都附带了标头和链接器存根(opengl32.lib),用于构建和链接使用OpenGL的可执行文件。

在macOS上设置现代OpenGL 4.1(Xcode,GLFW和GLEW)

1.安装GLFW

第一步是创建一个OpenGL窗口。 GLFW是一个开源的多平台库,用于使用OpenGL创建窗口,安装GLFW首先从www.glfw.org下载其文件

GLFW网页

提取GLFW文件夹,其内容将如下所示

GLFW文件夹内容

下载并安装CMake以构建GLFW。转到www.cmake.org/download/ ,下载CMake并安装MAC OS X.

CMake下载网页

如果没有安装Xcode。从Mac App Store下载并安装Xcode。

来自Mac App Store的Xcode

在GLFW文件夹内创建一个新的文件夹中生成

创建“Build”文件夹后的GLFW文件夹

打开CMake,单击Browse Source按钮选择GLFW文件夹(确保CMakeLists.txt)位于该文件夹内。之后,单击Browse Build按钮并在上一步中选择新创建的Build文件夹。

CMake路径

现在单击Configure按钮并选择Xcode as generator with Use default native compilers选项 ,然后单击Done

Xcode的Makefile

勾选BUILD_SHARED_LIBS选项,然后再次单击“ 配置”按钮,最后单击“ 生成”按钮。

选择BUILD_SHARED_LIBS

生成后,CMake应该是这样的

最终的CMake

现在打开Finder并转到/ usr ,创建一个本地文件夹名称(如果尚未存在)。打开本地文件夹并创建两个文件夹includelib(如果尚未存在)。

现在打开GLFW文件夹并转到Build (CMake已经构建了文件)。在Xcode中打开GLFW.xcodeproj文件。

Xcode项目文件

选择安装>我的Mac ,然后单击运行 (播放形状按钮)。

安装GLFW

它现在已成功安装(忽略警告)。

要确保Open Finder和goto / usr / local / lib文件夹以及三个GLFW库文件已经存在(如果没有那么打开GLFW文件夹中的Build文件夹并转到src / Debug将所有文件复制到/ usr / local / lib

GLFW Lib文件

打开Finder并转到/ usr / local / include和一个GLFW文件夹,其中有两个头文件,名称为glfw3.hglfw3native.h

GLFW头文件

2.安装GLEW

GLEW是一个跨平台的库,可以帮助查询和加载OpenGL扩展。它提供了运行时机制,用于确定目标平台上支持哪些OpenGL扩展。它仅适用于现代OpenGL(OpenGL 3.2及更高版本,需要在运行时确定功能)。要安装,首先从glew.sourceforge.net下载其文件

GLEW网页

提取GLFW文件夹,其内容将如下所示。

GLEW文件夹内容

现在打开终端,导航到GLEW文件夹并键入以下命令

make
sudo make install 
make clean
 

现在GLEW已成功安装。要确保安装它的Open Finder,请转到/ usr / local / include,并且其中已经存在一个GL文件夹,其中包含三个头文件,名称为glew.hglxew.hwglew.h

GLEW头文件

打开Finder并转到/ usr / local / lib ,GLEW库文件将在那里出现

GLEW库文件

3.测试并运行

现在我们已经成功安装了GLFW和GLEW。是时候编码了。打开Xcode并创建一个新的Xcode项目。选择命令行工具,然后继续,选择C ++作为语言。

Xcode项目

Xcode将创建一个新的命令行项目。

单击项目名称,然后在Build Settings选项卡下从Basic切换到All ,在Search Paths部分下,在Header Search Paths中添加/ usr / local / include并在Library Search Paths中添加/ usr / local / lib

搜索路径

单击项目名称,在Build Phases选项卡下,在Link With Binary Libraries下添加OpenGL.framework ,并从/ usr / local / lib添加最近创建的GLFWGLEW

链接二进制文件

现在我们准备使用C ++和Xcode在macOS上使用Modern Open GL 4.1进行编码。以下代码将使用带有空白屏幕输出的GLFW创建一个OpenGL窗口。

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

空白的OpenGL窗口