《OpenGL v3.3》——(1)搭建glfw3与glew环境,并创建第一个窗口

来源:互联网 发布:一致性哈希 java实现 编辑:程序博客网 时间:2024/06/05 10:07

《了解一些图形学知识》:

  1. 图形输送管道(pipeline既管线):指一堆原始数据途径一个输送管道,期间经过各种变化处理最终出现在屏幕上的像素的过程。
    3D坐标转换为2D坐标的处理是由OpenGL的pipeline管理的。
    pipeline可以被划分为两个主要部分:
    (1)把你的3D坐标转换为2D坐标; (2)把2D坐标转变为有实际的颜色的像素;

  2. 着色器(shader):pipeline可以被划分为几个阶段,每个阶段需要把前一个阶段的输出作为输入。所有这些阶段都是高度专门化的(它们持有各自特定的方法),它们能简单地并行执行。
    由于并行执行的特征,当今大多数显卡都有成千上万的小处理核心,在GPU上为每一个(输送管道pipeline的)阶段运行各自的小程序,从而在图形输送管道中快速处理你的数据。这些小程序叫做着色器。

    1. 图形输送管道(pipeline)的几个阶段为:顶点着色器(vertex shader)、基本图形组装(primitive assembly)、几何着色器(geometry shader) 细分着色器(tessellation shader)、像素化(rasterization光栅化、栅格化)、片元着色器(fragment shader)、alpha测试与混合(blending)。
    2. 而有些着着色器允许开发者自己配置,用我们自己写的着色器替换默认存在的。这样我们就能更细致地控制输送管道的特定部分了,因为它们运行在GPU上,它们也会节约宝贵的CPU时间。
    3. 着色器是由“着色器语言(GLSL)”写成的;
  3. 光栅化(rasterization):就是把矢量图或3维物体转化为像素的过程,即三维空间的物体转化为二维图形;

  4. 渲染管线(也就是上面的pipeline):三维物体渲染的过程,从三位物体最终渲染成图像的数据传输和处理;

  5. 开发者常用的着色器:顶点着色器、片元着色器;

  6. 标准化设备坐标:OpenGL只是在当它们的3个轴(x,y,z)在特定的-1.0到1.0的范围内时才处理3D坐标,这个范围的坐标就叫标准化设备坐标;

  7. 双缓冲(double buffer):当一个应用以单缓冲方式绘制的时候,图像会产生闪烁的问题。这是因为最后的图像输出不是被立即绘制出来的,而是一个像素一个像素绘制出来的,通常是从左到右从上到下的方式。
    由于这些图像不是立即呈现在用户面前的,而是一步一步地生成结果,这就产生很多不真实感。为了规避这些问题,窗口应用使用双缓冲的方式进行渲染。
    前缓冲包含最终的输出图像,它被显示在屏幕上,与此同时,所有的渲染命令绘制后缓冲。所有的缓冲渲染命令执行结束,我们就把后缓冲交换到前缓冲,这样图像就会立即显示到用户面前了,解决不真实感。


《了解一些glfw3的相关知识》

  • Window creation hints 窗口创建的提示
    在创建窗口和环境之前,可以设置许多提示(Hint)。一些影响窗口本身,另一些影响帧缓冲区或环境。每次使用glfwInit初始化库时,这些提示会都设置为它们的默认值,可以使用glfwWindowHint来单独设置,并可以使用glfwDefaultWindowHints立即重设为默认值;

《搭建glfw3与glew环境》:

  1. 下载 GLEW 与 GLFW3 的源代码,并编译:

    • 方法1:使用CMake生成对应VS与系统环境的工程文件(sln),然后通过sln工程编译出静态库(.lib);

    • 方法2:也可以编译成动态库(.dll);

  2. 指定 头文件目录 与 库目录:

    • 方法1:设置环境变量(因为太优雅,所以略过);

    • 方法2:将 头文件 和 库文件 放入VS编译器的搜寻目中;
       例如:头文件目录 C:\Program Files (x86)\Windows Kits\8.1\Include\um\gl;
          库文件目录 C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86;
          动态库目录 C:\Windows\SysWOW64
          (可以在VS中打开某个库,右键打开文件所在地址,就是头文件地址了)
       

    • 方法3:

      1. 创建一个文件(例如名include),将这两个库的所用到的头文件放进去。再创建一个文件(例如明lib),将这两个库的.lib静态库文件都放进去;
      2. “右键项目—属性—VC++目录—包含目录”=> 指定到include文件夹的地址;
      3. “右键项目—属性—VC++目录—库目录”=> 指定到lib文件夹的地址;
    • 方法4:将这些东西都放进你的工程里(不推荐)。

  3. 链接的几种方法:

    • 方法1:直接按照上面链接中,在“右键项目—属性—配置属性—链接器—输入—附加依赖项”处加上相关的库;(本次用到的是opengl32.lib,和glfw3.lib,glew32s.lib)

    • 方法2:在VS编译器中,添加代码#pragma comment(lib,”XXX.lib”)的方式加载lib文件;(本次用到的是opengl32.lib,和glfw3.lib,glew32s.lib)

  4. 补充:如果使用静态库,需要告诉编译器使用的是静态库

    • 方法1:在“右键项目—属性—C/C++—预处理器”的“预处理器定义”里GLEW_STATIC;

    • 方法2:在#include”glew.h”上一行添加 “#define GLEW_STATIC”;


《OpenGL库说明》:

  1. glfw3.h 窗口库

    • GLFW是一个C写的专门用于OpenGL开发的库,它只提供把物体渲染到屏幕所需的必要功能。它可以给我们创建一个OpenGL环境,定义窗口参数,以及相应用户输入,这些都是必要的功能。

    • GLUT并不建议使用,GLFW库目前不支持Android和IOS平台,SDL也不错稍微多一点学习成本但跨平台效果更好,当然还可以使用Qt。

  2. glew.h 扩展库

    • 由于OpenGL是一种标准/规范,它需要由驱动制造商在驱动中来实现这份特定的显卡支持规范。因为有许多不同版本的OpenGL驱动,OpenGL的大多数函数在编译时(compile-time)是未知状态的,需要在运行时(run-time)来请求。这就是开发者的任务了,开发者获取他/她所需要的函数的地址,把它们储存在函数指针中以备后用。

    • GLEW是OpenGL Extension Wrangler Library的缩写,它管理着之前我们提到的那件麻烦事。

    • 获取这些地址是依系统而定的,比如windows下:

// 定义函数原型typedef void ( * GL_GENBUFFERS) (GLsizei, GLuint * );// 寻找函数并分配给它一个函数指针GL_GENBUFFERS glGenBuffers = (GL_GENBUFFERS)wglGetProcAddress("glGenBuffers");// 现在函数可以正常调用了GLuint buffer;glGenBuffers(1, &buffer);

本篇所用的API:

《GLFW3 API》

  1. GLFWAPI int glfwInit(void);
    1. 参数:
      • none:
    2. 功能:
       This function initializes the GLFW library. Before most GLFW functions can be used, GLFW must be initialized, and before an application terminates GLFW should be terminated in order to free any resources allocated during or after initialization.
       此函数初始化GLFW库。在大多数GLFW函数能够使用之前,必须对GLFW进行初始化,并且在应用程序终止之前应该终止GLFW,以便释放在初始化期间或之后分配的任何资源。
       If this function fails, it calls glfwTerminate before returning. If it succeeds, you should call glfwTerminate before the application exits.
       如果该函数失败,它将在返回之前调用glfwTerminate。如果成功,您应该在应用程序退出之前调用glfwTerminate。
       
  2. GLFWAPI void glfwWindowHint(int hint, int value);

    1. 参数:
      • hint:The window hint to set.
      • value:The new value of the window hint.
    2. 功能:
       This function sets hints for the next call to glfwCreateWindow. The hints, once set, retain their values until changed by a call to glfwWindowHint or glfwDefaultWindowHints, or until the library is terminated.
       这个函数为下一次调用glfwCreateWindow设置了提示。这些提示,一旦设置,将保留它们的值,直到通过调用glfwWindowHint或glfwDefaultWindowHints函数更改时,或者直到该库终止。
       This function does not check whether the specified hint values are valid. If you set hints to invalid values this will instead be reported by the next call to glfwCreateWindow.
       这个函数不检查指定的提示值是否有效。如果您将“提示”设置为无效的值,那么将由下一个函数调用glfwCreateWindow来报告这个错误。
  3. GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos);

    1. 参数:
      • window:The window to query.
      • xpos:The x-coordinate of the upper-left corner of the client area.
      • ypos:The y-coordinate of the upper-left corner of the client area.
    2. 功能:
       This function sets the position, in screen coordinates, of the upper-left corner of the client area of the specified windowed mode window. If the window is a full screen window, this function does nothing.
       该函数用于设置在屏幕坐标中指定窗口模式的窗口的客户区域左上角的位置。如果窗口是一个全屏窗口,那么该函数什么也不做。
       Do not use this function to move an already visible window unless you have very good reasons for doing so, as it will confuse and annoy the user.
       不要使用这个函数来移动已经可见的窗口,除非您有很好的理由这样做,否则会使用户感到迷惑和烦恼。
       The window manager may put limits on what positions are allowed. GLFW cannot and should not override these limits.
       窗口管理器可能对所允许的位置设置限制。GLFW不能也不应该覆盖这些限制。
  4. GLFWAPI void glfwShowWindow(GLFWwindow* window);

    1. 参数:
      • window:The window to make visible.
    2. 功能:
       This function makes the specified window visible if it was previously hidden. If the window is already visible or is in full screen mode, this function does nothing.
       如果指定的窗口以前是隐藏的,该函数可使其为可见。如果窗口已经可见或处于全屏模式,该函数将不会执行任何操作。
  5. GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share);

    1. 参数:
      • width:The desired width, in screen coordinates, of the window. This must be greater than zero.
      • height:The desired height, in screen coordinates, of the window. This must be greater than zero.
      • title:The initial, UTF-8 encoded window title.
      • monitor:The monitor to use for full screen mode, or NULL for windowed mode.
      • share:The window whose context to share resources with, or NULL to not share resources.
    2. 功能:
       This function creates a window and its associated OpenGL or OpenGL ES context. Most of the options controlling how the window and its context should be created are specified with window hints.
       该函数创建一个窗口及其关联的OpenGL或OpenGL ES 环境。大多数控制窗口及其环境的选项都是通过窗口提示(Hint)指定的。
       Once you have created the window, you can switch it between windowed and full screen mode with glfwSetWindowMonitor. If the window has an OpenGL or OpenGL ES context, it will be unaffected.
       一旦您创建了这个窗口,您就可以使用glfwSetWindowMonitor函数,在窗口和全屏模式之间切换。如果窗口已存在一个OpenGL或OpenGL ES环境,这将不产生效果。
       By default, newly created windows use the placement recommended by the window system. To create the window at a specific position, make it initially invisible using the GLFW_VISIBLE window hint, set its position and then show it.
       默认情况下,新创建窗口的位置为视窗系统推荐的位置。要在一个特定的位置创建窗口,就要使用GLFW_VISIBLE窗口提示令它初始化时不可见,接着设置它的位置,然后显示它。(-。-事实上,我直接使用 glfwSetWindowPos(window, x, y) 就能改变位置了,也不懂官方文档中为啥搞那么麻烦)
       Returns:The handle of the created window, or NULL if an error occurred.
       返回值:成功则返回创建的窗口的句柄,出现错误则为NULL。
  6. GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window);

    1. 参数:
      • window:The window whose context to make current, or NULL to detach the current context.
    2. 功能:
       This function makes the OpenGL or OpenGL ES context of the specified window current on the calling thread. A context can only be made current on a single thread at a time and each thread can have only a single current context at a time.
       该函数指定在当前调用线程上的窗口渲染环境为OpenGL或OpenGL ES环境。单个线程上一次只能设置一个环境,而且每个线程一次只能有一个当前环境。
       By default, making a context non-current implicitly forces a pipeline flush. On machines that support GL_KHR_context_flush_control, you can control whether a context performs this flush by setting the GLFW_CONTEXT_RELEASE_BEHAVIOR window hint.
       默认情况下,使一个非当前的环境隐式地强制管线刷新。在支持GL_KHR_context_flush_control的机器上,您可以通过设置GLFW_CONTEXT_RELEASE_BEHAVIOR窗口提示(Hint)来控制渲染环境是否执行此刷新。
       The specified window must have an OpenGL or OpenGL ES context. Specifying a window without a context will generate a GLFW_NO_WINDOW_CONTEXT error.
       指定的窗口必须有一个OpenGL或OpenGL ES环境。如果指定一个没有环境的窗口将会产生一个GLFW_NO_WINDOW_CONTEXT错误。
  7. GLFWAPI void glfwTerminate(void);

    1. 参数:
      • none:
    2. 功能:
       This function destroys all remaining windows and cursors, restores any modified gamma ramps and frees any other allocated resources. Once this function is called, you must again call glfwInit successfully before you will be able to use most GLFW functions.
       这个函数会销毁所有剩下的窗口和游标,恢复所有被修改的伽马线,并释放所有其他分配的资源。一旦调用这个函数之后,您必须要再次调用glfwInit,才能使用大多数的GLFW函数。
       If GLFW has been successfully initialized, this function should be called before the application exits. If initialization fails, there is no need to call this function, as it is called by glfwInit before it returns failure.
       如果GLFW 已经成功的被初始化,在程序结束之前应该调用这个函数。如果初始化失败,就不需要调用这个函数,因为在glfwInit返回失败之前已经自行调用了。
  8. GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun);

    1. 参数:
      • window:The window whose callback to set.
      • cbfun:The new key callback, or NULL to remove the currently set callback.
    2. 功能:
       This function sets the key callback of the specified window, which is called when a key is pressed, repeated or released.
       该函数用来设置指定窗口的按键回调,当一个键被按下、重复或释放时调用。
       The key functions deal with physical keys, with layout independent key tokens named after their values in the standard US keyboard layout. If you want to input text, use the character callback instead.
       按键函数处理物理键,在标准的美式键盘布局中以其布局独立的按键符号命名。如果您想要输入文本,请使用glfwSetCharCallback 函数。
       When a window loses input focus, it will generate synthetic key release events for all pressed keys. You can tell these events from user-generated events by the fact that the synthetic ones are generated after the focus loss event has been processed, i.e. after the window focus callback has been called.
       当一个窗口失去输入焦点时,它将为所有按下的键生成合成的按键释放事件。您可以从用户生成的事件中分辨这些事件,事实上,一旦焦点失去的事件被执行后就生成该合成体,例如,在窗口焦点回调被调用之后。
       The scancode of a key is specific to that platform or sometimes even to that machine. Scancodes are intended to allow users to bind keys that don’t have a GLFW key token. Such keys have key set to GLFW_KEY_UNKNOWN, their state is not saved and so it cannot be queried with glfwGetKey.
       键的扫描码是特定于该平台的,甚至有时是对于该机器的。扫描码的目的是让用户能够绑定没有GLFW按键标记的按键。这样的键对于GLFW_KEY_UNKNOWN有关键的设置,它们的状态没有被保存,因此不能使用glfwGetKey查询。
       Sometimes GLFW needs to generate synthetic key events, in which case the scancode may be zero.
       有时GLFW需要生成合成的按键事件,在这种情况下,扫描代码可能为零。
  9. GLFWAPI int glfwWindowShouldClose(GLFWwindow* window);

    1. 参数:
      • window:The window to query.
    2. 功能:
       This function returns the value of the close flag of the specified window.
       该函数返回指定窗口的关闭标志的值。
  10. GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value);

    1. 参数:
      • window:The window whose flag to change.
      • value:The new value.
    2. 功能:
       This function sets the value of the close flag of the specified window. This can be used to override the user’s attempt to close the window, or to signal that it should be closed.
       该函数设置所指定的窗口的关闭标志的值。这可以用于覆盖用户尝试关闭该窗口的操作,或发出应该关闭窗口的信号。
  11. GLFWAPI void glfwPollEvents(void);

    1. 参数:
      • none:
    2. 功能:
       This function processes only those events that are already in the event queue and then returns immediately. Processing events will cause the window and input callbacks associated with those events to be called.
       此函数只处理已在事件队列中的事件,然后立即返回。处理事件将导与这些事件相关的窗口和输入回调被调用。
       On some platforms, a window move, resize or menu operation will cause event processing to block. This is due to how event processing is designed on those platforms. You can use the window refresh callback to redraw the contents of your window when necessary during such operations.
       在某些平台上,窗口移动、调整大小或菜单操作将导致事件处理阻塞。这是在于如何在这些平台上设计事件处理。在这些操作时,如有必要您可以使用窗口刷新回调来重新绘制窗口的内容。
       On some platforms, certain events are sent directly to the application without going through the event queue, causing callbacks to be called outside of a call to one of the event processing functions.
       在某些平台上,某些事件被直接发送到应用程序(例如SendMessage),而不需要经过事件队列,导致回调在某个事件处理函数的调用之外被调用。
      Event processing is not required for joystick input to work.
      对于操纵杆输入来说,事件处理是不需要的。
  12. GLFWAPI void glfwSwapBuffers(GLFWwindow* window);

    1. 参数:
      • window:window whose buffers to swap.
    2. 功能:
       This function swaps the front and back buffers of the specified window when rendering with OpenGL or OpenGL ES. If the swap interval is greater than zero, the GPU driver waits the specified number of screen updates before swapping the buffers.
       当使用OpenGL或OpenGL ES时,该函数会交换指定窗口的前和后缓冲区。如果交换间隔大于0,GPU驱动程序将在交换缓冲区之前等待指定的屏幕更新数。
       The specified window must have an OpenGL or OpenGL ES context. Specifying a window without a context will generate a GLFW_NO_WINDOW_CONTEXT error.
       指定的窗口必须有一个OpenGL或OpenGL ES环境。指定一个没有环境的窗口将会产生一个GLFW_NO_WINDOW_CONTEXT错误。
       This function does not apply to Vulkan.
       该函数不适用于Vulkan。

《OpenGL API》

  1. GLAPI void GLAPIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
    1. 参数:
      • x,y:Specify the lower left corner of the viewport rectangle, in pixels. The default is (0, 0).
      • width,height:Specify the width and height, respectively, of the viewport. When a GL context is first attached to a window, width and height are set to the dimensions of that window.
    2. 功能:
       glViewport specifies the affine transformation of x and y from normalized device coordinates to window coordinates. Let (xnd, ynd) be normalized device coordinates.
       该函数指定从标准化设备坐标到窗口坐标的x和y坐标的仿射变换。令(xnd,ynd)为标准化设备坐标。
       
  2. GLAPI void GLAPIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);

    1. 参数:
      • red、green、blue、alpha: Values specified by glClearColor are clamped to the range [0, 1].
    2. 功能:
       specifies the red, green, blue, and alpha values used by glClear to clear the color buffers.
       指定通过glClear来清空颜色缓冲区的红、绿、蓝 和 alpha值;
  3. GLAPI void GLAPIENTRY glClear (GLbitfield mask);

    1. 参数:
      • mask:Bitwise OR of masks that indicate the buffers to be cleared.
    2. 功能:
       takes a single argument that is the bitwise OR of several values indicating which buffer is to be cleared.
       接受一个参数,即位值或多个值,指示要清除哪个缓冲区。
       

代码:

Utility.h

#ifndef UTILITY_H#define UTILITY_H#include<windows.h>#include<iostream>#define GLEW_STATIC#include"glew.h" //这个要放在最前面,因为GLEW已经包含了OpenGL的头文件#include"glfw3.h"#endif

MyGame.cpp

#include"Utility.h"//自己定义一个回调函数;void key_callback(GLFWwindow * window, int key, int scancode, int action, int mode){    //当用户按下ESC,我们就把windowShouldClose设置为true,关闭应用    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)        glfwSetWindowShouldClose(window, GL_TRUE);}int __stdcall WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd){    //初始化GLFW    glfwInit();    //配置GLFW,第1个参数告诉我们打算配置哪个选项(枚举中选择,都带有GLFW_前缀),第2个参数(整数)代表我们为选项设置的值    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//这里的意思是告诉GLFW我们使用的版本是3.3    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//明确地使用core-profile,既当我们调用一个OpenGL旧遗留函数时会产生invalid operation错误;    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);//使用户不可以调整窗口大小;    glfwWindowHint(GLFW_VISIBLE, GL_FALSE);//初始是否显示    GLFWwindow * window  = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr, nullptr);//返回的参数,在后面的其他GLFW操作会需要它    glfwMakeContextCurrent(window);//告诉GLFW去创建我们的窗口环境,这个环境就是当前线程的主环境    glfwSetWindowPos(window, 100, 80);    glfwShowWindow(window);    if (window == NULL)    {        std::cout << "Failed to create GLFW window" << std::endl;        glfwTerminate();        return -1;    }    //GLEW管理着OpenGL的函数指针,所以我们希望在调用任何OpenGL函数之前初始化GLEW    glewExperimental = GL_TRUE;//设置为true可以保证GLEW使用更多的现代技术来管理OpenGL机能,不这么设置会使用默认的GL_FALSE,这样当使用core profile时可能发生问题;    if (glewInit() != GLEW_OK)    {        std::cout << "Failed to initialize GLEW" << std::endl;        return -1;    }    //Viewport(视口): 开始渲染前,必须告诉OpenGL渲染窗口的大小,这样OpenGL才知道我们希望如何设置窗口的大小和位置;    glViewport(0, 0, 800, 600);//前两个参数设置了窗口左下角位置,后两个是宽度和高度,它和GLFW窗口是一样大的.(可以设置为比GLFW更小的尺寸,这样OpenGL就会渲染在一个更小的窗口区域上,其它区域显示其他元素)    //注册回调函数;(在建立窗口之后 游戏初始化之前)    glfwSetKeyCallback(window, key_callback);    //游戏程序主循环;    while (!glfwWindowShouldClose(window))//此函数检测是否得到关闭指示,如果得到,则返回true;    {        //检查及调用事件;        glfwPollEvents();//检验是否有任何事件被触发,(如键盘鼠标移动),接着调用相应函数(我们可以通过回调方法设置它们)。        //这里是所有的渲染命令;        //用一种颜色来清空屏幕;        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);//这是一个状态设置函数;        glClear(GL_COLOR_BUFFER_BIT);//这是一个状态使用函数,它从当前状态获取清空所用的颜色;        //交换缓冲;        glfwSwapBuffers(window);//该函数会交换颜色缓冲,(颜色缓冲是一个GLFW窗口为每一个像素存储颜色数值的大缓冲)在本次迭代中绘制的,也作为输出显示在屏幕上;    }    glfwTerminate();    system("pause");    return 0;}

补充:

代码中有关GLEW的内容,以下链接已做详细解释。
OpenGL扩展库使用手册《GLEW—The OpenGL Extension Wrangler Library》

阅读全文
0 0
原创粉丝点击