本文记录VS 2022环境下配置openGL的流程,使用GLFW和GLEW第三方库。内容大致如下:
目录
1. 创建VS项目
创建一个VS空项目,勾选“将解决方案文件与项目置于同一目录”(可选项)。
在项目目录下新建文件夹"src", "lib", "include",分别用于存放源码、库文件和头文件。
其中,在"lib"文件夹下新建"debug", "release"文件夹,分别存放debug版本和release版本所使用的.lib文件。
2. 编译GLFW源码
首先,网络上通过GLFW、GLEW库的预编译来配置openGL的教程已经较多了,此处不再赘述,详情可以参考视频教程P2、P3部分
之所以要采用编译源码的方式配置环境,是由于我在通过预编译文件进行配置时,遇到了运行时冲突的warning,担心后续影响工作,所以决定自行编译,统一项目所用依赖库的运行时。
GLFW库的官网链接如下:
进入以后选择右上角的Download进入下载页面,或者直接点击右侧Download按钮下载源码。
进入下载页面以后,可以选择下载源码或者预编译文件,此处选择下载源码。
下载以后,解压缩文件,进入文件目录。文件目录内包含如下文件:
运行CMake工具,将CMakeList.txt所在目录输入到源码目录一栏("Where is the source code: ")。依照习惯,在恰当位置建立文件夹"build"用以存放生成的文件。将build文件夹路径输入到下一栏中。随后点击Configure按钮,进行生成配置。
选择项目的编译环境为"Visual Studio 17 2022",平台为win32(本案例以win32为所使用平台)。随后点击按钮“完成”。
待窗口中列出生成选项之后,注意到最后一个选项"USE_MSVC_RUNTIME_LIBRARY_DLL",此处若勾选为“是”,则编译中将使用动态运行时,若不勾选,则将使用静态运行时。由于考虑到使用动态运行时生成的程序,在转移到其他机器时容易因缺少运行时环境而无法运行,所以此处采用静态运行时,将运行时直接打包到生成的文件中。所以取消勾选该选项。
关于运行时可以参考此篇博客:带你玩转Visual Studio——带你跳出坑爹的Runtime Library坑
完成修改后,先点击Configure保存修改,再点击Generate生成文件。
随后在build文件夹中可以找到生成的Visual Studio项目。编译此项目即可完成GLFW的编译。
用VS 2022打开GLFW.sln文件。
在GLFW3/glfw文件上右键,选择propeties选项,打开属性界面。先在上方选择Configure为Debug,Platform为win32,转至C/C++ —> Code Generation —> Runtime Library,检查此项是否为Multi-threaded Debug(/MTd)。同理,再将上方Configure选为Release,检查此选项是否为Multi-threaded。
退出窗口,现在上方将VS模式选择为Debug, Win32,在左侧Solution "GLFW"上右键,选择Build Solution。随后再切换模式为Release, Win32,同样Build Solution。
编译完成后,将编译生成的.lib文件和头文件复制到自己的项目里面。
在Build目录下,进入src文件夹,内有两个目录,分别为"Debug", "Release",分别将两个文件夹内的文件复制到先前创立的VS项目中,"lib\debug"和"lib\release"目录下
又退回到CMakeList.txt所在目录下,进入include文件夹,将GLFW文件夹完整复制到自己所创建项目下的include文件夹内。
GLFW编译完成。
3. 编译GLEW源码
GLEW官方网站链接如下:
GLEW: The OpenGL Extension Wrangler Library (sourceforge.net)
进入以后,仿照先前流程,下载源码zip文件
下载之后,解压缩,进入目录。在沿路径build>vc12找到VS Solution文件,打开。
由于目前GLEW官方源码文件中的Solution文件最高仅到vc12,所以打开以后会有提示将项目进行重定向,选择“是”。
重定向之后,手动修改各项目的运行时库,依照GLFW配置时的操作,Debug版本使用(\MTd),Release版本使用(\MT),平台选择Win32。对四个项目都进行同样的配置。
设置完成后,仿照GLFW的构建,先选择Debug+win32模式进行Build,再选择Release+win32模式进行构建。注意,此处可能出现报错,如果Release模式构建成功一次以后,再次进行Build会产生“无法解释的外部符号”报错,原因未知。
两次Build均完成以后,退回目录"glew-2.1.0",进入"./lib/Debug/Win32"目录下,将一下两个文件复制到自建项目的"lib/debug"目录下
同理,将 "./lib/Release/Win32"的以下文件也复制到自建项目的"lib/release"目录下
4. 配置VS项目属性
在VS项目的src目录下,新建"application.cpp"文件。
完成以上步骤后,VS项目的目录应该呈现如下形式:
在项目标签上右键,选择Propeties,打开属性设置窗口:
首先将Configuration调整为All Configuration,Platform调整为Win32。
在C/C++ > Genral > Additional include Directories处添加"$(SolutionDir)include"。
在C/C++ > Preprocessor > Preprocessor Definitions处添加"GLEW_STATIC"(这是由于我们所使用的GLEW库是静态库版本,所以需要预先声明)
完成以上两项后,点击“应用(A)”。
随后将Configuration切换至Debug,Platform不改变。进入C/C++ > Code Generation > Runtime Library,设置为"(/MTd)"。
进入Linker > General > Additional Library Directories,添加"$(SolutionDir)lib\debug"。
进入Linker > Input > Additional Dependencies,添加"glew32sd.lib","glfw3.lib","opengl32.lib"。也就是将lib\debug下的所有.lib文件添加进去,并附加上一个opengl32.lib。
应用以上修改。再将Configuration改为"Release",同理进行以上操作,只不过将Runtime Library设置为"(/MT)" ,添加的Additional Library Directories为"$(SolutionDir)lib\release",添加的Additional Dependencies是"$(SolutionDir)lib\release"下的所有lib文件以及opengl32.lib文件。
最后,退出属性配置窗口,将以下测试代码复制到application.cpp文件中:
#include <GL\glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"out vec3 ourColor;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n"
" ourColor = aColor;\n"
"}\0";
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(ourColor, 1.0f);\n"
"}\n\0";
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glew: initialize library
// ---------------------------------------
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize GLEW" << std::endl;
return -1;
}
// build and compile our shader program
// ------------------------------------
// vertex shader
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"
<< infoLog << std::endl;
}
// fragment shader
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n"
<< infoLog << std::endl;
}
// link shaders
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n"
<< infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
// positions // colors
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom left
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // top
};
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
// glBindVertexArray(0);
// as we only have a single shader, we could also just activate our shader once beforehand if we want to
glUseProgram(shaderProgram);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// render the triangle
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
分别在Release模式和Debug模式下尝试生成解决方案:
如果无报错,尝试通过 Local Windows Debugger运行程序。若配置成功,则将显示如下三角形,并且不会闪退: