自学内容网 自学内容网

OpenGL笔记十之Shader类的封装

OpenGL笔记十之Shader类的封装

—— 2024-07-10 晚上

总结自bilibili赵新政老师的教程

code review!

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.txt

10 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 colors
float 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到当前的vao
glBindBuffer(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 绑定当前的program
shader->begin();

//2 绑定当前的vao
GL_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();//开始使用当前Shader

void end();//结束使用当前Shader

private:
//shader program
//type:COMPILE LINK
void 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代码字符串的两个string
std::string vertexCode;
std::string fragmentCode;

//声明用于读取vs跟fs文件的inFileStream
std::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})

11.2.build中可以找到assets

在这里插入图片描述


原文地址:https://blog.csdn.net/weixin_43297891/article/details/140365490

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!