一、跨平台环境基本配置
1、环境搭建
1)linux OpenGL环境搭建参考:ubuntu18.04 OpenGL开发(显示YUV)_ubuntu opengl-CSDN博客
https://blog.51cto.com/cerana/6433535
本文使用的是QTCreator
2)windows下环境搭建
OpenGL+Visual Studio2022+GLFW+glad详细配置教程_给vs配置glfw和glad-CSDN博客
本文是用的是QTCreator或者vs2022。
2、创建cmake
这里测试使用的图形界面使用QT。其中路径后面讲解。
主要有几个注意事项:
1)linux下需要添加
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-error=register")
2)windows下需要设置vitual studio
set(LIBRARY_GLFW_PATH "${PROJECT_SOURCE_DIR}/lib-vc2022")
cmake_minimum_required(VERSION 3.5)
SET(TARGET "QTGL")
project(${TARGET} VERSION 0.1 LANGUAGES CXX)
#设置QT安装路径
if(WIN32)
set(QT5_CMAKE_PATH "D:\\Qt\\Qt5.14.2\\5.14.2\\msvc2017_64\\lib\\cmake")
elseif()
endif()
set(Qt5_DIR "${QT5_CMAKE_PATH}/Qt5")
set(Qt5Core_DIR "${QT5_CMAKE_PATH}/Qt5Core")
set(Qt5Widgets_DIR "${QT5_CMAKE_PATH}/Qt5Widgets")
set(Qt5UiTools_DIR "${QT5_CMAKE_PATH}/Qt5UiTools")
set(Qt5PrintSupport_DIR "${QT5_CMAKE_PATH}/Qt5PrintSupport")
set(Qt5Svg_DIR "${QT5_CMAKE_PATH}/Qt5Svg")
#
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(WIN32)
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-error=register")
endif()
set(QTGL_GLAPI_PATH "${PROJECT_SOURCE_DIR}/GLAPI")
set(GLAD_FOLDER_INCLUDE_PATH "${PROJECT_SOURCE_DIR}/glad/include")
set(GLAD_FOLDER_SOURCE_PATH "${PROJECT_SOURCE_DIR}/glad/src")
set(GLM_FOLDER_INCLUDE_PATH "${PROJECT_SOURCE_DIR}/glm")
set(GLFW_FOLDER_INCLUDE_PATH "${QTGL_GLAPI_PATH}/glfw")
if(WIN32)
set(LIBRARY_GLFW_PATH "${PROJECT_SOURCE_DIR}/lib-vc2022")
LINK_DIRECTORIES("${LIBRARY_GLFW_PATH}")
endif()
include_directories(${QTGL_GLAPI_PATH})
include_directories(${GLAD_FOLDER_INCLUDE_PATH})
include_directories(${GLM_FOLDER_INCLUDE_PATH})
include_directories(${GLFW_FOLDER_INCLUDE_PATH})
file(GLOB GLAPI_HEADERS "${QTGL_GLAPI_PATH}/*.h")
source_group("GLAPI/include" FILES ${GLAPI_HEADERS})
file(GLOB GLAPI_SOURCES "${QTGL_GLAPI_PATH}/*.cpp")
source_group("GLAPI/source" FILES ${GLAPI_SOURCES})
file(GLOB_RECURSE GLAD_INCLUDE "${GLAD_FOLDER_INCLUDE_PATH}/*.*")
source_group("glad/include" FILES ${GLAD_INCLUDE})
file(GLOB GLAD_SOURCE "${GLAD_FOLDER_SOURCE_PATH}/*.*")
source_group("glad/src" FILES ${GLAD_SOURCE})
SET(QTUI_PATH "${PROJECT_SOURCE_DIR}/QTUI")
file(GLOB_RECURSE QTUI_FILES "${QTUI_PATH}/*.*")
source_group("QTUI" FILES ${QTUI_FILES})
set(ALL_SOURCES
${GLAPI_HEADERS}
${GLAPI_SOURCES}
${GLAD_INCLUDE}
${GLAD_SOURCE}
${QTUI_FILES}
)
ADD_EXECUTABLE(${TARGET} WIN32 ${ALL_SOURCES})
find_package(Qt5 COMPONENTS Widgets Core Gui REQUIRED)
target_link_libraries(${TARGET} PRIVATE Qt5::Widgets Qt5::Core Qt5::Gui)
if(WIN32)
target_link_libraries(${TARGET} PRIVATE debug "${LIBRARY_GLFW_PATH}/glfw3.lib" optimized "${LIBRARY_GLFW_PATH}/glfw3.lib")
target_link_libraries(${TARGET} PRIVATE debug "${LIBRARY_GLFW_PATH}/glfw3_mt.lib" optimized "${LIBRARY_GLFW_PATH}/glfw3_mt.lib")
target_link_libraries(${TARGET} PRIVATE debug "${LIBRARY_GLFW_PATH}/glfw3dll.lib" optimized "${LIBRARY_GLFW_PATH}/glfw3dll.lib")
else()
target_link_libraries(${TARGET} PRIVATE glfw3)
target_link_libraries(${TARGET} PRIVATE GL)
target_link_libraries(${TARGET} PRIVATE X11)
target_link_libraries(${TARGET} PRIVATE m)
target_link_libraries(${TARGET} PRIVATE pthread)
target_link_libraries(${TARGET} PRIVATE dl)
endif()
3、第三方库文件
1)glad和glm
glad下载:https://glad.dav1d.de/,下载后得到glad.zip
把glad整个文件夹放置到工程目录下,然后,把src下的glad.c改为glad.cpp。
把glm整个文件夹放置到工程目录下,不需要额外的修改。
2)glfw
glfw下载,Download | GLFW
windows下,需要将编译好的glfw库放置到工程下。如图:
3)整体工程结构如图:
4、QT界面文件QTUI
创建一个QDialog或者QMainWindow,带有.ui文件,可以使用uic指令将其转换为.h文件。
cmake中需要配置QT安装路径: Qt5_DIR关键字,同时需要配置QT5相关的模块:
find_package(Qt5 COMPONENTS Widgets Core Gui REQUIRED)
target_link_libraries(${TARGET} PRIVATE Qt5::Widgets Qt5::Core Qt5::Gui)
注意:QT6的配置和QT5不同,这里用的QT5。
二、OPENGL 简单示例
1、初始化工程
1)初始化glfw,具体解释看:OpenGL学习(一) 绘制一个三角形 - 简书
int DrawApi::initialGlfw()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glad: load all OpenGL function pointers
return 0;
}
2)创建窗口:
int DrawApi::createWindow(string windowName, OUT GLFWwindow*& window)
{
// glfw window creation
// --------------------
window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, windowName.c_str(), NULL, NULL);
if (window == NULL)
{
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
cout << "Failed to initialize GLAD" << endl;
return -1;
}
glViewport(0, 0, 800, 800);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 窗体能够根据拉动变化
return 0;
}
3)顶点着色器
int DrawApi::createVertexShader(OUT GLuint& vertexShader, const char* vertexShaderSource)
{
//1.初始化着色器
//创建一个着色器类型
vertexShader = glCreateShader(GL_VERTEX_SHADER);
//把代码复制进着色器中
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
//编译顶点着色器
glCompileShader(vertexShader);
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
//判断是否编译成功
if (!success) {
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
cout << "error when vertex compile:" << infoLog << endl;
return -1;
}
return 0;
}
4)片元着色器
int DrawApi::createFragmentShader(OUT GLuint& fragmentShader, const char* fragmentShaderSource)
{
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
int success;
char infoLog[512];
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
//判断是否编译成功
if (!success) {
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
cout << "error when fragment compile:" << infoLog << endl;
return -1;
}
return 0;
}
5)编译程序
int DrawApi::createProgram(GLuint& vertexShader, GLuint& fragmentShader, OUT GLuint& shaderProgram)
{
//链接,创建一个程序
shaderProgram = glCreateProgram();
//链接上共享库
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
//链接
glLinkProgram(shaderProgram);
int success;
char infoLog[512];
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
cout << "error when link compile:" << infoLog << endl;
return -1;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return 0;
}
6)绘制三角形
int DrawApi::drawTriangles(const vector<vector<float>>& triangelValues)
{
//1.会先绑定顶点
//2.绑定缓冲区
//3.接着把顶点输入到顶点缓冲去中
//4.把缓冲区的数据传送到着色器中
//5.输出到屏幕。
if (initialGlfw() < 0)
return -1;
GLFWwindow* window = nullptr;
if (createWindow("learnOpenGL", window) < 0)
return -2;
GLuint vertexShader;
if (createVertexShader(vertexShader, VERTEX_SHADER) < 0)
return -3;
GLuint fragmentShader;
if (createFragmentShader(fragmentShader, FRAGMENT_SHADER) < 0)
return -4;
GLuint shaderProgram;
if (createProgram(vertexShader, fragmentShader, shaderProgram) < 0)
return -5;
if (triangelValues.size() == 0)
return -6;
if (triangelValues.size() > 1256)
return -7;
float vertices[4096] = {0};
for (int i = 0; i < triangelValues.size(); i++)
{
for (int j = 0; j < 3; j++)
{
vertices[3 * i + j] = triangelValues[i][j];
}
}
GLuint VAO;
// 生成分配VAO
glGenVertexArrays(1, &VAO);
// 绑定VAO,注意在core模式,没有绑定VAO,opengl拒绝绘制任何东西
glBindVertexArray(VAO);
GLuint VBO;
// 生成一个VBO缓存对象
glGenBuffers(1, &VBO);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 类型为GL_ARRAY_BUFFER 第二第三参数说明要放入缓存的多少,GL_STATIC_DRAW当画面不动的时候推荐使用
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
processInput(window);
// render
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
//绑定数据
glBindVertexArray(VAO);
//绘制一个三角形
//从0开始,3个
glDrawArrays(GL_TRIANGLES, 0, triangelValues.size() * 3);
glBindVertexArray(0);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
2、读取shader文件
shader着色器文件保存成vsCode能识别的js文件。如图:
编写读取函数:
// 返回错误码,strPath为文件路径,shaderTxt返回的文件内容
std::string readShader(std::string strPath, std::string& shaderTxt)
{
std::ifstream ifs;
ifs.open(strPath, std::ios::in);
if (!ifs.is_open())
{
return "file open failed";
}
std::string textContent;
while (getline(ifs, textContent))
{
shaderTxt += textContent;
shaderTxt += '\n';
}
return "";
}
使用shader代码示例:
GLuint vertexShader;
std::string strVertexShader;
std::string errStr = m_shaderManger.readShader(m_shaderManger.getShaderPath() + "VERTEX_SHADER.js", strVertexShader);
if (errStr.length() > 0)
{
return -1;
}
if (createVertexShader(vertexShader, strVertexShader.c_str()) < 0)
return -3;
std::string strFragmentShader;
errStr = m_shaderManger.readShader(m_shaderManger.getShaderPath() + "FRAGMENT_SHADER.js", strFragmentShader);
if (errStr.length() > 0)
{
return -1;
}
GLuint fragmentShader;
if (createFragmentShader(fragmentShader, strFragmentShader.c_str()) < 0)
return -4;
3、创建三角形的代码示例
这是创建有颜色的三角形其中Point和Color为自定义结构体
class Color
{
public:
float m_r;
float m_g;
float m_b;
Color(float r, float g, float b)
{
m_r = r;
m_g = g;
m_b = b;
};
void setColor(float r, float g, float b)
{
m_r = r;
m_g = g;
m_b = b;
};
};
class Point
{
public:
float m_x;
float m_y;
float m_z;
Point(float x, float y, float z)
{
m_x = x;
m_y = y;
m_z = z;
};
void setPoint(float x, float y, float z)
{
m_x = x;
m_y = y;
m_z = z;
};
};
函数入参设置成vector,每一个值即为一个带颜色的点,每三个值就是一个三角形。因此使用的时候可以传入多个三角形。
int DrawApi::drawColorTriangle(vector < std::pair< Point, Color > > triPoints)
{
if (initialGlfw() < 0)
return -1;
GLFWwindow* window = nullptr;
if (createWindow("learnOpenGL", window) < 0)
return -2;
GLuint vertexShader;
if (createVertexShader(vertexShader, VERTEX_COLOR_SHADER) < 0)
return -3;
GLuint fragmentShader;
if (createFragmentShader(fragmentShader, FRAGMENT_COLOR_SHADER) < 0)
return -4;
GLuint shaderProgram;
if (createProgram(vertexShader, fragmentShader, shaderProgram) < 0)
return -5;
if (triPoints.size() != 3)
return -6;
float vertices[18] = {0};
int vertiCnt = 0;
for (int i = 0; i < triPoints.size(); i++)
{
vertices[vertiCnt++] = triPoints[i].first.m_x;
vertices[vertiCnt++] = triPoints[i].first.m_y;
vertices[vertiCnt++] = triPoints[i].first.m_z;
vertices[vertiCnt++] = triPoints[i].second.m_r;
vertices[vertiCnt++] = triPoints[i].second.m_g;
vertices[vertiCnt++] = triPoints[i].second.m_b;
}
GLuint VAO;
// 生成分配VAO
glGenVertexArrays(1, &VAO);
// 绑定VAO,注意在core模式,没有绑定VAO,opengl拒绝绘制任何东西
glBindVertexArray(VAO);
GLuint VBO;
// 生成一个VBO缓存对象
glGenBuffers(1, &VBO);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 类型为GL_ARRAY_BUFFER 第二第三参数说明要放入缓存的多少,GL_STATIC_DRAW当画面不动的时候推荐使用
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//第五个参数表示了一开始读取数据时的偏移量,坐标不需要变动,颜色则是离开头偏移了3个loat
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
//glBindBuffer(GL_ARRAY_BUFFER, 0);
//glBindVertexArray(0);
//glBindVertexArray(1);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
processInput(window);
// render
/* glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);*/
glUseProgram(shaderProgram);
//绑定数据
glBindVertexArray(VAO);
//绘制一个三角形
//从0开始,3个
glDrawArrays(GL_TRIANGLES/*GL_TRIANGLES*/, 0, 3);
glBindVertexArray(0);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
//float timeValue = glfwGetTime(); //获取时间
//float redValue = (sin(timeValue) / 3.0f) + 0.6f;
//float greenValue = (sin(timeValue) / 2.0f) + 0.5f; //利用sin和时间实现颜色值在0-1之间变换
//float blueValue = sin(timeValue);
//int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor"); //获取shader中的ourColor变量
//glUniform4f(vertexColorLocation, redValue, greenValue, blueValue, 1.0f); //修改ourColor的值
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
使用时如下所示:
{
vector < std::pair< Point, Color > > triPoints;
triPoints.push_back({Point(-0.5, -0.5, 0.3), Color(1.0, 0.0, 0.0)});
triPoints.push_back({Point(0.5, -0.5, 0.3), Color(0.0, 1.0, 0.0)});
triPoints.push_back({Point(0.0, 0.5, -0.3), Color(0.3, 0.0, 1.0) });
m_drawApi.drawColorTriangle(triPoints);
}