OpenGL 环境搭建

网上有对于OpenGL的环境搭建,讲的不是很清晰,也踩了很多坑,在小茗大哥的帮助下终于搞好了。

首先要搞清楚OpenGL, glut, freeglut, glfw, glad这些到底是什么。

对于OpenGL,wiki上讲的很全面了:

OpenGL(英语:Open Graphics Library,译名:开放图形库或者“开放式图形库”)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。

OpenGL被设计为只有输出的,所以它只提供渲染功能。核心API没有窗口系统、音频、打印、键盘/鼠标或其他输入设备的概念。

因此,OpenGL实际上一些具有渲染2D,3D图形的API,想要完成更多关于窗口、音频、打印、键鼠交互等功能,得和其他一些库搭配使用

glew(The OpenGL Extension Wrangler Library)是对底层OpenGL接口的封装,可以让你的代码跨平台。

Freeglut(OpenGL Utility Toolkit)主要用于创建OpenGL上下文(context)、接收一些鼠标键盘事件等等。

旧一些的教程,包括OpenGL红宝书都是使用glew + freeglut这种方案。然而他们已经不再更新了(从官网可以看出,最新的freeglut是在2015年发布,glew则在2017年)。

glad与glew作用相同,可以看作它的升级版。

glfw(Graphics Library Framework)是Freeglut升级版,作用基本一样。

建议使用glad+glfw配合使用,可以参考LearnOpenGL第三节上的教程来搭建环境。

GLAD+GLFW

VS2019

在LearnOpenGL的第三节详细的介绍了Windows下使用VS2015搭建OpenGL环境的方法,我用相同的方法在vs2019上也成功搭建了环境,只需在Cmake的时候把版本改成vs2019。

Clion

使用Clion则要复杂很多,感谢@domain大哥提供的帮助

首先我们要设置好MinGW的工具链,用过Clion的同学应该都设置好了。

编译glfw

glfw官网下载glfw的源代码,然后下载cmake。cmake安装的时候注意选择x86_64。

首先设定好输入输出目录。输入目录就是glfw-3.3文件夹,输出目录可以选择在同目录新建一个build文件夹。

接下来点Configure,选择MinGW Makefiles,点确定,然后再点一次Configure(使中间的内容从红色变白即可。

中间的CMAKE_INSTALL_PREFIX是编译好的输出目录,可以修改成你想要的目录。

然后Generate,在之前的输出目录build文件夹内就有MinGW的Makefile了。

然后管理员模式启动命令行,进入build文件夹,然后:

1
2
ming32-make.exe -j8
ming32-make.exe install

我也不明白为什么是ming32-make。如果你没有ming32-make.exe的话,可能是你没有将MinGW放入环境变量。

在之前设定好的CMAKE_INSTALL_PREFIX目录下,你应该会得到:

1
2
3
4
5
6
7
├─GLFW  
  ├─include
  │  └─GLFW
  └─lib
    ├─cmake
    │  └─glfw3
    └─pkgconfig

下载glad

glad并不需要我们编译,glad使用了一个在线服务帮你生成对应版本的glad。

API-gl选择版本3.3,Profile选择Core,Options勾上Generate a loader,其余默认,点击generate,下载zip并解压

为方便管理各种第三方库,建议将之前编译的GLFW和这里下载的GLAD统一放在一个Library文件夹里。

编写Cmakelist.txt

这个过程比较复杂,@domian 茗哥写了一份写好的,可以直接用

大概就是先要find到package,然后添加包含目录和链接目录,再链接。注意这里的斜杠与反斜杠方向特别的坑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
cmake_minimum_required(VERSION 3.14)
project(triangle)

set(CMAKE_CXX_STANDARD 14)

set(glfw3_DIR "D:\\Library\\GLFW\\lib\\cmake\\glfw3")
find_package(glfw3 3.3 REQUIRED)
find_package(OpenGL REQUIRED)

include_directories("D:\\Library\\GLFW\\include")
include_directories("D:\\Library\\glad\\include")
link_libraries("D:/Library/GLFW/lib/libglfw3.a")

add_executable(triangle main.cpp glad.c)

target_include_directories(triangle PUBLIC ${OPENGL_INCLUDE_DIR})
target_link_libraries(triangle glfw ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY})

注意修改工程名,这里的triangle,GLFW路径,对应编译好的GLFW文件夹,glad路径,对应下载下来的glad文件夹。

然后把glad/src文件夹内的glad.c文件放在Clion工程文件夹内,记得Reloade一下CMake Project。

测试demo

附一份测试demo

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
// #include <cstdint>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

const char* vertexShaderSource = "#version 330 core\n"
                                 "layout (location = 0) in vec3 aPos;\n"
                                 "void main()\n"
                                 "{\n"
                                 "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
                                 "}\0";
const char* fragmentShaderSource = "#version 330 core\n"
                                   "out vec4 FragColor;\n"
                                   "void main()\n"
                                   "{\n"
                                   "   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
                                   "}\n\0";


float vertices[] = {
        0.5f, 0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        -0.5f,  0.5f, 0.0f
};

uint32_t indices[] = {
        0, 1, 3,
        1, 2, 3
};

int main() {
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    GLFWwindow* window = glfwCreateWindow(800, 800, "OpenGL", NULL, NULL);
    if (window == nullptr) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    GLuint vertexShader;
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    GLint success;
    GLchar infolog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, nullptr, infolog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILD\n" << infolog << std::endl;
        return -1;
    }

    GLuint fragmentShader;
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, nullptr, infolog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILD\n" << infolog << std::endl;
        return -1;
    }

    GLuint shaderProgram;
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, fragmentShader);
    glAttachShader(shaderProgram, vertexShader);
    glLinkProgram(shaderProgram);
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, nullptr, infolog);
        std::cout << "ERROR::PROGRAM::SHADER::LINKING_FAILD\n" << infolog << std::endl;
        return -1;
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    glViewport(0, 0, 800, 800);

    GLuint VBO, VAO, EBO;
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)nullptr);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    while (!glfwWindowShouldClose(window)) {
        processInput(window);

        glClearColor(0.2f, 0.5f, 0.8f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    glfwTerminate();
    return 0;
}


void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    //call back when user change the size of the window.
    glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window) {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

复制进main.cpp,然后Run。不出意外的话能得到一个蓝底橙色的正方形矩形窗口。

glew + Freeglut

鉴于学校教学使用的是这套方案,所以也尝试搭建了相关的环境。

VS2019

采用与Learn OpenGL中一样的方式编译,添加包含目录与库目录。官方的源码里已经有了vs2012的工程文件,直接编译即可。修改的目录时候注意窗口上方显示正在修改的版本,Release/Build,x86/x64,建议全部都修改好,不然当你使用了一个没有修改过的配置的时候,就会include不了正确的头文件。

Clion

这一部分的坑就更多了,首先到官网下好载最新的freeglut和glew的源码。freeglut解压的时候报了莫名其妙的错误,看了一下只有一个lnk文件解压不出来,基本不影响使用。

编译freeglut与glew

编译freeglut比较简单,和之前一样,直接用Cmake就可以生成Makefile,然后去输出目录执行mingw32-make.exemingw32-make.exe install,得到的目录结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
freeglut/
├── bin
│   └── libfreeglut.dll
├── include
│   └── GL
│       ├── freeglut.h
│       ├── freeglut_ext.h
│       ├── freeglut_std.h
│       └── glut.h
└── lib
    ├── libfreeglut.dll.a
    ├── libfreeglut_static.a
    └── pkgconfig
        └── freeglut.pc

编译glew的时候会有点奇怪。

源码文件夹内应该有一个auto文件夹,我们先要在auto目录下,用wsl,即linux子系统执行makemake install,然后才能在build文件夹中Cmake,剩下的就跟之前一样了,用Cmake生成Makefile,然后用mingw32-make.exe来make和make install。得到的目录结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
glew/
├── bin
│   ├── glew32.dll
│   ├── glewinfo.exe
│   └── visualinfo.exe
├── include
│   └── GL
│       ├── glew.h
│       ├── glxew.h
│       └── wglew.h
└── lib
    ├── cmake
    │   └── glew
    │       ├── CopyImportedTargetProperties.cmake
    │       ├── glew-config.cmake
    │       ├── glew-targets-release.cmake
    │       └── glew-targets.cmake
    ├── libglew32.a
    ├── libglew32.dll.a
    └── pkgconfig
        └── glew.pc

然后把glew32.dlllibfreeglut.dll两个文件放到任意一个在Path的目录下,可以是C:\Windows\SysWow64,当然如果你不想让你的Windows目录这么乱的话,建议放在mingw的bin文件夹内(它当然在Path)。

编写Cmakelist.txt

接下来就可以用Clion新建工程了。写好的Cmakelist.txt如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
cmake_minimum_required(VERSION 3.14)
project(triangle-fg)

set(CMAKE_CXX_STANDARD 14)

find_package(OpenGL REQUIRED)

include_directories("D:\\Library\\freeglut\\include")
include_directories("D:\\Library\\glew\\include")
link_libraries("D:/Library/glew/lib/libglew32.a")
link_libraries("D:/Library/glew/lib/libglew32.dll.a")
link_libraries("D:/Library/freeglut/lib/libfreeglut.dll.a")
link_libraries("D:/Library/freeglut/lib/libfreeglut_static.a")

add_executable(triangle-fg main.cpp InitShader.cpp)

target_include_directories(triangle-fg PUBLIC ${OPENGL_INCLUDE_DIR})
target_link_libraries(triangle-fg ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY})

与之前的没太大区别

测试demo

接下来再稍微修改下老师给的demo代码。开头的include改成:

1
#include "include/Angel.h"

当然你也可以选择在Cmakelist.txt中添加额外包含目录

读取着色器文件的地方也要稍微改下,改成:

1
    GLuint program = InitShader("../vshader.glsl", "../fshader.glsl");

这是因为最终生成的二进制文件是放在cmake-build-debug文件夹中的,而两个着色器文件放在它的上一级目录。

构建并运行,不出意外的话你就能得到一个红色三角形了。

评论