1. 基础环境
2. 项目结构
拆分窗口创建、渲染逻辑等核心功能
增加glwindow.h与glwindow.cpp文件,xmake配置不变
bash
opengl-xmake-demo/
├─ include/ # 头文件目录
│ ├─ ***
│ └─ glwindow.h # OpenGL核心功能模块
├─ src/
│ ├─ ***
│ ├─ glwindow.cpp # OpenGL核心功能模块
│ └─ main.cpp # 主程序代码(测试代码见下文)
└─ xmake.lua # XMake 配置文件(核心)
3. 源代码
glwindow.h
cpp
#ifndef GLWINDOW_H
#define GLWINDOW_H
#include <iostream>
#include <string>
#include <GLFW/glfw3.h>
class GLWindow
{
public:
GLWindow(int w, int h, const std::string &title);
~GLWindow();
void render();
void update();
bool shouldClose();
void processInput();
private:
// 窗口相关信息
GLFWwindow *window;
int width;
int height;
std::string title;
// 渲染相关
unsigned int shaderProgram;
unsigned int VBO, VAO;
private:
bool initGLFW();
bool initGLAD();
void initShaders();
void initBuffers();
// 窗口大小变化回调函数(静态成员函数)
static void framebufferSizeCallback(GLFWwindow *window, int width, int height);
};
#endif // GLWINDOW_H
glwindow.cpp
cpp
// glad必须在glwindow.h之前包含
#include <glad/glad.h>
#include "glwindow.h"
GLWindow::GLWindow(int w, int h, const std::string &title) : width(w), height(h), title(title)
{
if (!initGLFW())
{
throw std::runtime_error("GLFW初始化失败!");
}
if (!initGLAD())
{
throw std::runtime_error("GLAD初始化失败!");
}
initShaders();
initBuffers();
}
GLWindow::~GLWindow()
{
// 释放VAO、VBO和着色器程序
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
// 销毁窗口并终止GLFW
glfwDestroyWindow(window);
glfwTerminate();
}
/* 渲染函数 */
void GLWindow::render()
{
// 清空屏幕
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 使用着色器程序并绘制三角形
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
/* 更新函数 */
void GLWindow::update()
{
glfwSwapBuffers(window);
glfwPollEvents();
}
/* 检查窗口是否应该关闭 */
bool GLWindow::shouldClose()
{
return glfwWindowShouldClose(window);
}
/* 处理输入 */
void GLWindow::processInput()
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
}
/* 初始化GLFW */
bool GLWindow::initGLFW()
{
// 初始化GLFW
if (!glfwInit())
{
std::cerr << "GLFW初始化失败!" << std::endl;
return false;
}
// 设置OpenGL版本和配置文件
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 创建窗口
window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
if (!window)
{
std::cerr << "窗口创建失败!" << std::endl;
glfwTerminate();
return false;
}
// 绑定窗口上下文并设置大小回调
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);
// 将当前GLWindow对象指针存入窗口用户数据(供回调使用)
glfwSetWindowUserPointer(window, this);
return true;
}
/* 初始化GLAD*/
bool GLWindow::initGLAD()
{
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cerr << "Glad加载失败!" << std::endl;
return false;
}
// 设置视口大小(初始窗口大小)
glViewport(0, 0, width, height);
return true;
}
/* 初始化着色器程序 */
void GLWindow::initShaders()
{
// 顶点着色器源码
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"
"in vec3 ourColor;\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(ourColor, 1.0);\n"
"}\0";
// 编译顶点着色器
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glCompileShader(vertexShader);
// 检查顶点着色器错误
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
std::cerr << "顶点着色器编译失败:\n"
<< infoLog << std::endl;
}
// 编译片段着色器
unsigned int 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::cerr << "片段着色器编译失败:\n"
<< infoLog << std::endl;
}
// 链接着色器程序
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// 检查链接错误
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
std::cerr << "着色器程序链接失败:\n"
<< infoLog << std::endl;
}
// 删除临时着色器对象
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
/* 初始化缓冲区对象 */
void GLWindow::initBuffers()
{
// 顶点数据(位置+颜色)
float vertices[] = {
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下:红
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下:绿
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部:蓝
};
// 生成VAO和VBO
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// 绑定VAO(必须先绑定VAO才能配置顶点属性)
glBindVertexArray(VAO);
// 绑定VBO并复制数据到GPU
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 配置位置属性(location=0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
// 配置颜色属性(location=1)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// 解绑缓冲(可选,避免误操作)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
/* 窗口大小变化回调函数 */
void GLWindow::framebufferSizeCallback(GLFWwindow *window, int width, int height)
{
// 从窗口用户数据中获取GLWindow对象指针
GLWindow *glWindow = static_cast<GLWindow *>(glfwGetWindowUserPointer(window));
if (glWindow)
{
// 更新窗口大小并设置视口
glWindow->width = width;
glWindow->height = height;
glViewport(0, 0, width, height);
}
}
main.cpp
cpp
#include <iostream>
#include "glwindow.h"
int main()
{
// 创建窗口(宽800,高600,标题"Triangle Demo")
GLWindow window(800, 600, "Triangle Demo");
// 渲染循环
while (!window.shouldClose())
{
window.processInput(); // 处理输入
window.render(); // 渲染一帧
window.update(); // 交换缓冲区和事件处理
}
return 0;
}
4. 结果示例
