OpenGL笔记十之Shader类的封装
—— 2024-07-10 晚上
bilibili赵新政老师的教程看后笔记
code review!
文章目录
- OpenGL笔记十之Shader类的封装
- 1.运行
- 2.目录结构
- 3.main.cpp
- 4.application
- 4.1.CMakeLists.txt
- 4.2.Application.h
- 4.3.Application.cpp
- 5.assets
- 5.1.shaders:vertex.glsl
- 5.2.shaders:fragment.glsl
- 6.glframework
- 6.1.CMakeLists.txt
- 6.2.core.h
- 6.3.shader.h
- 6.4.shader.cpp
- 7.wrapper
- 7.1.CMakeLists.txt
- 7.2.checkError.h
- 7.3.checkError.cpp
- 8.glad.c:略
- 9.主CMakeLists.txt
- 10.thirdParty
- 10.1.glad.h:略
- 10.2.khrplatform.h:略
- 11.CMake中的关键使用:拷贝assets文件夹到CMAKE_BINARY_DIR
- 11.1.代码
- 11.2.build中可以找到assets
1.运行
2.目录结构
.
└── 11_OpenGL_ShaderClass├── application│ ├── Application.cpp│ ├── Application.h│ └── CMakeLists.txt├── assets│ └── shaders│ ├── fragment.glsl│ └── vertex.glsl├── CMakeLists.txt├── glad.c├── glframework│ ├── CMakeLists.txt│ ├── core.h│ ├── shader.cpp│ └── shader.h├── main.cpp├── thirdParty│ └── include│ ├── glad│ │ └── glad.h│ └── KHR│ └── khrplatform.h└── wrapper├── checkError.cpp├── checkError.h└── CMakeLists.txt10 directories, 17 files
3.main.cpp
#include <iostream>#include "glframework/core.h"
#include "glframework/shader.h"
#include <string>
#include <assert.h>//断言
#include "wrapper/checkError.h"
#include "application/Application.h"/*
*┌────────────────────────────────────────────────┐
*│ 目 标: Shader类编写
*│ 讲 师: 赵新政(Carma Zhao)
*│ 拆分目标:
*│ 1 实现Shader文件的读取
*│ 2 实现Shader内容的编译链接
*│ 3 实现Shader查错的函数
*└────────────────────────────────────────────────┘
*/GLuint vao;
Shader* shader = nullptr;void OnResize(int width, int height) {GL_CALL(glViewport(0, 0, width, height));std::cout << "OnResize" << std::endl;
}void OnKey(int key, int action, int mods) {std::cout << key << std::endl;
}void prepareVAO() {//1 准备positions colorsfloat positions[] = {-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f,0.0f, 0.5f, 0.0f,};float colors[] = {1.0f, 0.0f,0.0f,0.0f, 1.0f,0.0f,0.0f, 0.0f,1.0f};unsigned int indices[] = {0, 1, 2};//2 VBO创建GLuint posVbo, colorVbo;glGenBuffers(1, &posVbo);glBindBuffer(GL_ARRAY_BUFFER, posVbo);glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);glGenBuffers(1, &colorVbo);glBindBuffer(GL_ARRAY_BUFFER, colorVbo);glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);//3 EBO创建GLuint ebo;glGenBuffers(1, &ebo);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//4 VAO创建glGenVertexArrays(1, &vao);glBindVertexArray(vao);//5 绑定vbo ebo 加入属性描述信息//5.1 加入位置属性描述信息glBindBuffer(GL_ARRAY_BUFFER, posVbo);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);//5.2 加入颜色属性描述数据glBindBuffer(GL_ARRAY_BUFFER, colorVbo);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);//5.3 加入ebo到当前的vaoglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);glBindVertexArray(0);
}void prepareShader() {shader = new Shader("assets/shaders/vertex.glsl","assets/shaders/fragment.glsl");
}void render() {//执行opengl画布清理操作GL_CALL(glClear(GL_COLOR_BUFFER_BIT));//1 绑定当前的programshader->begin();//2 绑定当前的vaoGL_CALL(glBindVertexArray(vao));//3 发出绘制指令glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);glBindVertexArray(0);shader->end();
}int main() {if (!app->init(800, 600)) {return -1;}app->setResizeCallback(OnResize);app->setKeyBoardCallback(OnKey);//设置opengl视口以及清理颜色GL_CALL(glViewport(0, 0, 800, 600));GL_CALL(glClearColor(0.2f, 0.3f, 0.3f, 1.0f));prepareShader();prepareVAO();while (app->update()) {render();}app->destroy();return 0;
}
4.application
4.1.CMakeLists.txt
#递归将本文件夹下所有cpp放到FUNCS中
file(GLOB_RECURSE APP ./ *.cpp)#将FUNCS中所有cpp编译为funcs这个lib库
add_library(app ${APP} )target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(app PRIVATE glfw)
4.2.Application.h
#pragma once /*
*┌────────────────────────────────────────────────┐
*│ 目 标: 封装Application(表示了当前应用程序本身)
*│ 讲 师: 赵新政(Carma Zhao)
*│ 拆分目标:
*│
*│ 1 单例类(全局唯一实例)
*│ 2 成员变量 + 成员函数
* 2.1 成员函数-init(初始化)
* 2.2 成员函数-update(每一帧执行)
* 2.3 成员函数-destroy(结尾执行)
*│ 3 响应回调函数(Resize)
* 3.1 声明一个函数指针ResizeCallback
* 3.2 声明一个ResizeCallback类型的成员变量
* 3.3 声明一个SetResizeCallback的函数 ,设置窗体变化响应回调函数
* 3.4 声明一个static的静态函数,用于响应glfw窗体变化
* 3.5 将静态函数设置到glfw的监听Resize监听当中
* 3.6 * 学会使用glfw的UserPointer
*│ 4 响应键盘消息函数(KeyBoard)
* 3.1 声明一个static的静态函数,用于响应glfw的键盘事件
* 3.2 将静态函数设置到glfw的监听KeyCallback监听当中
* 3.3 声明一个函数指针KeyBoardCallback
* 3.4 声明一个KeyBoardCallback类型的成员变量
* 3.5 声明一个SetKeyBoardCallback的函数 ,设置键盘响应回调函数
* 3.6 * 学会使用glfw的UserPointer
*└────────────────────────────────────────────────┘
*/
#include <iostream>#define app Application::getInstance()class GLFWwindow;using ResizeCallback = void(*)(int width, int height);
using KeyBoardCallback = void(*)(int key, int action, int mods);class Application {
public:~Application();//用于访问实例的静态函数static Application* getInstance();bool init(const int& width = 800, const int& height = 600);bool update();void destroy();uint32_t getWidth()const { return mWidth; }uint32_t getHeight()const { return mHeight; }void setResizeCallback(ResizeCallback callback) { mResizeCallback = callback; }void setKeyBoardCallback(KeyBoardCallback callback) { mKeyBoardCallback = callback; }private://C++类内函数指针static void frameBufferSizeCallback(GLFWwindow* window, int width, int height);static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);private://全局唯一的静态变量实例static Application* mInstance;uint32_t mWidth{ 0 };uint32_t mHeight{ 0 };GLFWwindow* mWindow{ nullptr };ResizeCallback mResizeCallback{ nullptr };KeyBoardCallback mKeyBoardCallback{ nullptr };Application();
};
4.3.Application.cpp
#include "Application.h"
#include<glad/glad.h>
#include<GLFW/glfw3.h>//初始化Application的静态变量
Application* Application::mInstance = nullptr;
Application* Application::getInstance() {//如果mInstance已经实例化了(new出来了),就直接返回//否则需要先new出来,再返回if (mInstance == nullptr) {mInstance = new Application();}return mInstance;
}Application::Application() {}Application::~Application() {}bool Application::init(const int& width, const int& height) {mWidth = width;mHeight = height;//1 初始化GLFW基本环境glfwInit();//1.1 设置OpenGL主版本号、次版本号glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//1.2 设置OpenGL启用核心模式(非立即渲染模式)glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//2 创建窗体对象mWindow = glfwCreateWindow(mWidth, mHeight, "OpenGLStudy", NULL, NULL);if (mWindow == NULL) {return false;}//**设置当前窗体对象为OpenGL的绘制舞台glfwMakeContextCurrent(mWindow);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {std::cout << "Failed to initialize GLAD" << std::endl;return false;}glfwSetFramebufferSizeCallback(mWindow, frameBufferSizeCallback);//this就是当前全局唯一的Application对象glfwSetWindowUserPointer(mWindow, this);//键盘响应glfwSetKeyCallback(mWindow, keyCallback);return true;
}bool Application::update() {if (glfwWindowShouldClose(mWindow)) {return false;}//接收并分发窗体消息//检查消息队列是否有需要处理的鼠标、键盘等消息//如果有的话就将消息批量处理,清空队列glfwPollEvents();//切换双缓存glfwSwapBuffers(mWindow);return true;
}void Application::destroy() {//退出程序前做相关清理glfwTerminate();
}void Application::frameBufferSizeCallback(GLFWwindow* window, int width, int height) {std::cout << "Resize" << std::endl;Application* self = (Application*)glfwGetWindowUserPointer(window);if (self->mResizeCallback != nullptr) {self->mResizeCallback(width, height);}//if (Application::getInstance()->mResizeCallback != nullptr) {// Application::getInstance()->mResizeCallback(width, height);//}
}void Application::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {Application* self = (Application*)glfwGetWindowUserPointer(window);if (self->mKeyBoardCallback != nullptr) {self->mKeyBoardCallback(key, action, mods);}
}
5.assets
5.1.shaders:vertex.glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec3 color;
void main()
{gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);color = aColor;
}
5.2.shaders:fragment.glsl
#version 330 core
out vec4 FragColor;
in vec3 color;
void main()
{FragColor = vec4(color, 1.0f);
}
6.glframework
6.1.CMakeLists.txt
file(GLOB_RECURSE FW ./ *.cpp)add_library(fw ${FW} )
6.2.core.h
#pragma once//注意:glad头文件必须在glfw引用之前引用
#include <glad/glad.h>
#include <GLFW/glfw3.h>
6.3.shader.h
#pragma once#include "core.h"
#include<string>class Shader {
public:Shader(const char* vertexPath, const char* fragmentPath);~Shader();void begin();//开始使用当前Shadervoid end();//结束使用当前Shaderprivate://shader program//type:COMPILE LINKvoid checkShaderErrors(GLuint target,std::string type);private:GLuint mProgram{ 0 };
};
6.4.shader.cpp
#include"shader.h"
#include"../wrapper/checkError.h"#include<string>
#include<fstream>
#include<sstream>
#include<iostream>Shader::Shader(const char* vertexPath, const char* fragmentPath) {//声明装入shader代码字符串的两个stringstd::string vertexCode;std::string fragmentCode;//声明用于读取vs跟fs文件的inFileStreamstd::ifstream vShaderFile;std::ifstream fShaderFile;//保证ifstream遇到问题的时候可以抛出异常vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);try {//1 打开文件vShaderFile.open(vertexPath);fShaderFile.open(fragmentPath);//2 将文件输入流当中的字符串输入到stringStream里面std::stringstream vShaderStream, fShaderStream;vShaderStream << vShaderFile.rdbuf();fShaderStream << fShaderFile.rdbuf();//3 关闭文件vShaderFile.close();fShaderFile.close();//4 将字符串从stringStream当中读取出来,转化到code String当中vertexCode = vShaderStream.str();fragmentCode = fShaderStream.str();}catch (std::ifstream::failure& e) {std::cout << "ERROR: Shader File Error: " << e.what() << std::endl;}const char* vertexShaderSource = vertexCode.c_str();const char* fragmentShaderSource = fragmentCode.c_str();//1 创建Shader程序(vs、fs)GLuint vertex, fragment;vertex = glCreateShader(GL_VERTEX_SHADER);fragment = glCreateShader(GL_FRAGMENT_SHADER);//2 为shader程序输入shader代码glShaderSource(vertex, 1, &vertexShaderSource, NULL);glShaderSource(fragment, 1, &fragmentShaderSource, NULL);//3 执行shader代码编译 glCompileShader(vertex);//检查vertex编译结果checkShaderErrors(vertex, "COMPILE");glCompileShader(fragment);//检查fragment编译结果checkShaderErrors(fragment, "COMPILE");//4 创建一个Program壳子mProgram = glCreateProgram();//6 将vs与fs编译好的结果放到program这个壳子里glAttachShader(mProgram, vertex);glAttachShader(mProgram, fragment);//7 执行program的链接操作,形成最终可执行shader程序glLinkProgram(mProgram);//检查链接错误checkShaderErrors(mProgram, "LINK");//清理glDeleteShader(vertex);glDeleteShader(fragment);
}
Shader::~Shader() {}void Shader::begin() {GL_CALL(glUseProgram(mProgram));
}void Shader::end() {GL_CALL(glUseProgram(0));
}void Shader::checkShaderErrors(GLuint target, std::string type) {int success = 0;char infoLog[1024];if (type == "COMPILE") {glGetShaderiv(target, GL_COMPILE_STATUS, &success);if (!success) {glGetShaderInfoLog(target, 1024, NULL, infoLog);std::cout << "Error: SHADER COMPILE ERROR" << "\n" << infoLog << std::endl;}}else if (type == "LINK") {glGetProgramiv(target, GL_LINK_STATUS, &success);if (!success) {glGetProgramInfoLog(target, 1024, NULL, infoLog);std::cout << "Error: SHADER LINK ERROR " << "\n" << infoLog << std::endl;}}else {std::cout << "Error: Check shader errors Type is wrong" << std::endl;}
}
7.wrapper
7.1.CMakeLists.txt
#递归将本文件夹下所有cpp放到FUNCS中
file(GLOB_RECURSE WRAPPER ./ *.cpp)#将FUNCS中所有cpp编译为funcs这个lib库
add_library(wrapper ${WRAPPER} )target_include_directories(wrapper PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(wrapper PRIVATE glfw)
7.2.checkError.h
#pragma once //预编译宏
#ifdef DEBUG
#define GL_CALL(func) func;checkError();
#else
#define GL_CALL(func) func;
#endif void checkError();
7.3.checkError.cpp
#include "checkError.h"
#include <glad/glad.h>
#include <string>
#include <iostream>
#include <assert.h>void checkError() {GLenum errorCode = glGetError();std::string error = "";if (errorCode != GL_NO_ERROR) {switch (errorCode){case GL_INVALID_ENUM: error = "INVALID_ENUM"; break;case GL_INVALID_VALUE: error = "INVALID_VALUE"; break;case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break;case GL_OUT_OF_MEMORY: error = "OUT OF MEMORY"; break;default:error = "UNKNOWN";break;}std::cout << error << std::endl;//assert会根据传入的bool值,来决定程序是否停止//true:程序顺利运行//false:程序会断死assert(false);}
}
8.glad.c:略
9.主CMakeLists.txt
# 指定 CMake 最低版本
cmake_minimum_required(VERSION 3.12)
add_definitions (-DDEBUG)# 项目名称
project(OpenGL_Lecture)# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)file(GLOB ASSETS "./assets" )file(COPY ${ASSETS} DESTINATION ${CMAKE_BINARY_DIR})# 包含头文件目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/thirdParty/include/usr/include
)# 包含库文件目录
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/thirdParty/lib/usr/lib/x86_64-linux-gnu
)# 设置 CMAKE_PREFIX_PATH 以找到 GLFW
set(CMAKE_PREFIX_PATH "/usr/local/lib/cmake/glfw3")
set(GLFW_DIR "/usr/local/lib/cmake/glfw3")# 查找 GLFW3 库
find_package(glfw3 REQUIRED CONFIG)add_subdirectory(wrapper)
add_subdirectory(application)
add_subdirectory(glframework)# 添加可执行文件
add_executable(openglStudy "main.cpp" "glad.c")# 链接库
target_link_libraries(openglStudy glfw wrapper app fw)
10.thirdParty
10.1.glad.h:略
10.2.khrplatform.h:略
11.CMake中的关键使用:拷贝assets文件夹到CMAKE_BINARY_DIR
11.1.代码
file(GLOB ASSETS "./assets" )
file(COPY ${ASSETS} DESTINATION ${CMAKE_BINARY_DIR})