OpenGL学习笔记(1)

发布于 2024-08-08  2342 次阅读


最近尝试手搓一个简单的gis系统,但是最简单的矢量数据绘制就遇到了麻烦,用qt的QPaintWidget套件性能更不上,我也不想用qgis做二次开发,于是开始学opengl。一开始想着直接看个qt opengl的教学,但是绘制三角形这步实在学不明白,于是还是沉下心来找个opengl教程好好学一下。

【B站最好】OpenGL小白到精通系列-保姆级-计算机图形学_哔哩哔哩_bilibili

B站最好的OpenGL教程,我觉得名副其实,不仅教OpenGL,还教工程管理的知识,收获良多。

chap01 OpenGL简介,图形学基础知识

这部分没啥记录的,配合Games101挺好懂的。

Chap02 Cmake,GLFW,GLAD,项目环境搭建

这部分也没啥好说的,复习一下Cmake。不过以前用cmake都是看别人的项目,这次还是跟着老师用cmake从头构建一个项目,以前都是VS,sln懒狗操作。

Chap03 基础窗体搭建

main函数里构建最简单的对象,加上基础事件响应,缩放和键盘

事件响应就是调用glfw的函数,把自己设置的相应规则的函数指针传进去

接着让glad加载当前版本所有opengl函数

之后是最简单的opengl api,设置画布清理颜色和清理画布

#include <iostream>
#include<glad/glad.h>
#include <GLFW/glfw3.h>
//声明且实现一个窗体大小变化的函数
void frameBufferSizeCallBack(GLFWwindow* window, int width, int height)
{
	std::cout << "窗体最新大小:" << width << "," << height << std::endl;
}
//声明且实现一个键盘消息回调函数
void keyCallBack(GLFWwindow* window,int key,int scancode,int action,int mods)
{
	if (key==GLFW_KEY_W)
	{
		//触发了v
	}
	if (action==GLFW_PRESS)
	{
		//键位按下
	}
	if (action==GLFW_RELEASE)
	{
		//键位抬起
	}
	if (mods == GLFW_MOD_CONTROL)
	{
		//按下ctrl的同时,按下key
	}
	if (mods==GLFW_MOD_SHIFT)
	{
		//按下了shift的同时,按下了key
	}
	std::cout << "按下了:" << key << std::endl;
	std::cout << "action:" << action << std::endl;
	std::cout << "mods:" << mods << std::endl;
}
//创建窗体
int main() 
{
	//1 初始化GLFW
	glfwInit();
	//1.1 设置OpenGL主板本号,次版本号
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
	//1.2 设置OpenGL启用核心模式(非立即渲染模式)
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	//2 创建窗体对象
	GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGLStudy", NULL, NULL);
	//设置当前窗体对象为OpenGL的绘制舞台
	glfwMakeContextCurrent(window);
	//设置监听,监听窗体大小变化的消息
	glfwSetFramebufferSizeCallback(window, frameBufferSizeCallBack);
	//设置监听,监听键盘消息
	glfwSetKeyCallback(window, keyCallBack);
	//*****使用glad加载所有openGL的函数
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}
        //设置opengl视口和清理颜色
        glViewport(0, 0, 800, 600);
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
	//3 执行窗口循环
	while (!glfwWindowShouldClose(window))
	{
		//接受并发窗口消息
		//检测消息队列是否有需要处理的鼠标、键盘等消息
		//如果有的话就将消息批量处理,清空队列
		glfwPollEvents();
	}
	//4 退出程序前做相关清理
	glfwTerminate();
	return 0;
}

Chap04 错误检查

OpenGL提供了错误检查函数,配合assert.h,返回最近发生的错误

GLenum glGetError();

每用一个opengl api后面都加一句错误检查就太麻烦了,把错误检查代码抽出来,单独作为一个代码文件,并通过lib连接。用宏定义做替换,通过GL_CALL(func)的方式进行检查。此外编译可以通过debug选项选择是否启动检查。

main.cpp
#include <iostream>
#include<glad/glad.h>
#include <GLFW/glfw3.h>
#include<String>
#include<assert.h>//断言
#include "wrapper/checkError.h"
//目标:openGL错误检查
//1.体验glGetError
//2.将错误检测代码封装为函数
//3.将错误检查代码放到其他cpp文件
//4.创建了GL_CALL这个宏,便捷进行gl函数查错
//5.在CMakeLists当中使用全局预编译宏,来控制是否开启错误检查
//声明且实现一个窗体大小变化的函数
void frameBufferSizeCallBack(GLFWwindow* window, int width, int height)
{
	std::cout << "窗体最新大小:" << width << "," << height << std::endl;
	glViewport(0, 0, width, height);
}
//声明且实现一个键盘消息回调函数
void keyCallBack(GLFWwindow* window,int key,int scancode,int action,int mods)
{
	if (key==GLFW_KEY_W)
	{
		//触发了v
	}
	if (action==GLFW_PRESS)
	{
		//键位按下
	}
	if (action==GLFW_RELEASE)
	{
		//键位抬起
	}
	if (mods == GLFW_MOD_CONTROL)
	{
		//按下ctrl的同时,按下key
	}
	if (mods==GLFW_MOD_SHIFT)
	{
		//按下了shift的同时,按下了key
	}
	std::cout << "按下了:" << key << std::endl;
	std::cout << "action:" << action << std::endl;
	std::cout << "mods:" << mods << std::endl;
}
//创建窗体
int main() 
{
	//1 初始化GLFW
	glfwInit();
	//1.1 设置OpenGL主板本号,次版本号
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
	//1.2 设置OpenGL启用核心模式(非立即渲染模式)
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	//2 创建窗体对象
	GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGLStudy", NULL, NULL);
	//设置当前窗体对象为OpenGL的绘制舞台
	glfwMakeContextCurrent(window);
	//设置监听,监听窗体大小变化的消息
	glfwSetFramebufferSizeCallback(window, frameBufferSizeCallBack);
	//设置监听,监听键盘消息
	glfwSetKeyCallback(window, keyCallBack);
	//*****使用glad加载所有openGL的函数
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}
	//设置openGL视口以及清理颜色
	glViewport(0, 0, 800, 600);
	glClearColor(0.1f,0.3f,0.3f,1.0f);
	//3 执行窗口循环
	while (!glfwWindowShouldClose(window))
	{
		//接受并发窗口消息
		//检测消息队列是否有需要处理的鼠标、键盘等消息
		//如果有的话就将消息批量处理,清空队列
		glfwPollEvents();
		//执行opengl画布清理操作
		GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
		//GL_CALL(glClear(-1));
		//checkError();
		//渲染操作
		// 
		//执行双循环
		glfwSwapBuffers(window);
	}
	//4 退出程序前做相关清理
	glfwTerminate();
	return 0;
}
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);
	}
}
checkError.h
#pragma once //防止重编译
//预编译宏
#ifdef DEBUG
#define GL_CALL(func) func;checkError();
#else
#define GL_CALL(func) func;
#endif // DEBUG
void checkError();
CMakeLists.txt
#需求的最低cmake程序版本
cmake_minimum_required(VERSION 3.12)
#本工程的名字
project(OpenGL_Lecture )
#本工程支持的C++版本
set(CMAKE_CXX_STANDARD 17)
#往项目中加入一个全局的预编译宏
add_definitions(-DDEBUG)
include_directories(system ${CMAKE_CURRENT_SOURCE_DIR}/thirdParty/include)
link_directories(system ${CMAKE_CURRENT_SOURCE_DIR}/thirdParty/lib)
add_subdirectory(wrapper)
#本工程所有cpp文件编译链接,生成exe
add_executable(glStudy "main.cpp" "glad.c" )
 target_link_libraries(glStudy glfw3.lib wrapper)
CMakeLists.txt
#递归将本文件夹下所有cpp放到WRAPPER中
file(GLOB_RECURSE WRAPPER ./ *.cpp)
#将FUNCS中所有cpp编译为wpapper这个lib库
add_library(wrapper ${WRAPPER} )

运用宏是挺方便的,其实也可以通过完美转发来实现,实现代码也是看的眼花

// 完美转发实现
template<typename Func, typename... Args>
auto GLCall(Func&& func, Args&&... args) -> decltype(auto) {
	if constexpr (std::is_void_v<decltype(func(std::forward<Args>(args)...))>) {
		std::forward<Func>(func)(std::forward<Args>(args)...);
		checkError();
	}
	else {
		auto result = std::forward<Func>(func)(std::forward<Args>(args)...);
		checkError();
		return result;
	}
}

chap05 application封装

把glfw相关的窗口步骤封装到类,运用单例模式,并通过init,update,destory三个函数调用,而不是传统的实例类的模式。这种方法对单例类的创建和释放把控比较灵活。

由于glfw的窗口被封装了,那么事件响应也麻烦一点,虽然听课的时候有点懵,但顺着代码逻辑走一遍就清楚了。没封装之前直接调用glfw的函数把自己写的响应函数指针传过去就行,但是封装后window指针拿不到了,在application实例类里,所以先发给实例类,让实例类再转发一遍。步骤复杂的原因是glfw的响应函数在init阶段就设置了,但main里init之后才传过去具体实现的函数指针。所以在application类中用个函数指针作为成员变量(mfunc)接着。先用个static函数(成员函数指针不方便传,static函数可以当作普通函数指针)先传给glfw,static函数里用mfunc去处理,一开始传给glfw时还是空指针,但main里传过去之后mfunc就指向main里我们实现的响应函数了,等到触发事件再去调用时就能正常调用了。

main.cpp
#include <iostream>
#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include<String>
#include<assert.h>//断言
#include "application/application.h"
#include "wrapper/checkError.h"

void OnResize(int width, int height) {
    std::cout << "width:" << width << " height:" << height << std::endl;
    GL_CALL(glViewport(0, 0, width, height));
}

void OnKey(int key, int scancode, int action, int mods) {
    std::cout << "按下了:" << key << std::endl;
    std::cout << "action:" << action << std::endl;
    std::cout << "mods:" << mods << std::endl;
}

int main(){
    if (!app->init(800, 600)) {
        return -1;
    }

    app->setResizeCallback(OnResize);
    app->setKeyCallback(OnKey);

    //设置opengl视口和清理颜色
    glViewport(0, 0, 800, 600);
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

    //执行窗体循环
    while (app->update()) {
        
        //执行OpenGL画布清理操作
        GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
        //调用完美转发实现
        //GLCall(glClear,-1);

        //渲染操作
    }

    //退出前清理
    app->destory();

    return 0;
}  

application.h
#pragma once
#include <iostream>
//#include "GLFW/glfw3.h"

#define app Application::getInstance()

struct GLFWwindow;
using ResizeCallback = void(*)(int, int);
using KeyCallback = void(*)(int, int, int, int);

//单例模式
class Application {
public:
	~Application();

	//用于访问实例的静态函数
	static Application* getInstance();

	uint32_t getWidth() const { return mWidth; }
	uint32_t getHeight() const { return mHeight; }

	void setResizeCallback(ResizeCallback callback) { mResizeCallback = callback; }
	void setKeyCallback(KeyCallback keyCallback) { mKeyCallback = keyCallback;}

	bool init(const int&,const int&);

	bool update();

	void destory();

private:
	static void frameBufferSizeCallback(GLFWwindow* window, int width, int height);
	static void frameKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
private:
	//全局唯一的静态变量实例
	static Application* mInstance;

	std::uint32_t mWidth{0};
	std::uint32_t mHeight{0};
	GLFWwindow* mWindow{ nullptr };

	ResizeCallback mResizeCallback = nullptr;
	KeyCallback mKeyCallback = nullptr;

	Application();
};

application.cpp
#include "application.h"
#include "glad/glad.h"
#include "GLFW/glfw3.h"

//初始化Application的静态变量
Application* Application::mInstance = nullptr;
Application* Application::getInstance() {
	if (mInstance == nullptr) {
		mInstance = new Application();
	}
	return mInstance;
}

Application::Application() {

}

Application::~Application() {

}

bool Application::init(const int& width = 800,  const int& height = 600) {
    mWidth = width;
    mHeight = height;

    //初始化GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    //创建窗体对象
    mWindow = glfwCreateWindow(mWidth, mHeight, "OpenGLStuty", NULL, NULL);
    if (mWindow == NULL)
        return false;
    glfwMakeContextCurrent(mWindow);
    //*****使用glad加载所有openGL的函数
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    glfwSetFramebufferSizeCallback(mWindow, frameBufferSizeCallback);
    glfwSetKeyCallback(mWindow, frameKeyCallback);

    glfwSetWindowUserPointer(mWindow, this);

    return true;
}

bool Application::update() {
    if (glfwWindowShouldClose(mWindow)) {
        return false;
    }
    //接收并分发窗体消息
    glfwPollEvents();
    //调用完美转发实现
    //GLCall(glClear,-1);

    //切换双缓存
    glfwSwapBuffers(mWindow);

    return true;
}

void Application::destory() {
    //退出前清理
    glfwTerminate();
}

void Application::frameBufferSizeCallback(GLFWwindow* window, int width, int height) {
    //if (Application::getInstance()->mResizeCallback != nullptr) {
    //    Application::getInstance()->mResizeCallback(width, height);
    //}
    Application* self = (Application*)glfwGetWindowUserPointer(window);
    if (self->mResizeCallback != nullptr) {
        self->mResizeCallback(width, height);
    }
}

void Application::frameKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    Application* self = (Application*)glfwGetWindowUserPointer(window);
    if (self->mKeyCallback != nullptr) {
        self->mKeyCallback(key,scancode,action,mods);
    }
}

chap06 VBO

VBO的创建,绑定,存储

void prepare() {
    float vertices[] = {
    -0.5,-0.5f,0.0f,
    0.5f,-0.5f,0.0f,
    0.0f,0.5f,0.0f
    };
    //1 生成一个vbo
    GLuint vbo = 0;
    GL_CALL(glGenBuffers(1, &vbo));
    //2 绑定当前vbo,到openGL状态机的当前vbo插槽上
    GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
    //3 向当前vbo传输数据,也是在开辟显存
    GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));
}

void prepareSingleBuffer() {
    //1 准备顶点位置数据与颜色数据
    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
    };

    //2 为位置&颜色数据各自生成一个vbo
    GLuint posVbo=0, colorVbo =0;
    GL_CALL(glGenBuffers(1,&posVbo));
    GL_CALL(glGenBuffers(1,&colorVbo));

    //3 给两个分开的vbo各自填充数据
    //position填充数据
    glBindBuffer(GL_ARRAY_BUFFER, posVbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
    //color填充数据
    glBindBuffer(GL_ARRAY_BUFFER,colorVbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
}

//交叉存储
void prepareInterleavedBuffer() {
    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
    };
    GLuint vbo = 0;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_STATIC_DRAW, sizeof(vertices), vertices, GL_ARRAY_BUFFER);
}

chap07 VAO

void prepareSingleBuffer() {
	//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
	};


	//2 使用数据生成两个vbo posVbo, colorVbo
	GLuint posVbo, colorVbo;
	glGenBuffers(1, &posVbo);
	glGenBuffers(1, &colorVbo);

	glBindBuffer(GL_ARRAY_BUFFER, posVbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);

	//3 生成vao并且绑定
	GLuint vao = 0;
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	//4 分别将位置/颜色属性的描述信息加入vao当中
	//4.1描述位置属性
	glBindBuffer(GL_ARRAY_BUFFER, posVbo);//只有绑定了posVbo,下面的属性描述才会与此vbo相关
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

	//4.2 描述颜色属性
	glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

	glBindVertexArray(0);
}

void prepareInterleavedBuffer() {
	//1 准备好Interleaved数据(位置+颜色)
	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
	};


	//2 创建唯一的vbo
	GLuint vbo = 0;
	GL_CALL(glGenBuffers(1, &vbo));
	GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
	GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));

	//3 创建并绑定vao
	GLuint vao = 0;
	GL_CALL(glGenVertexArrays(1, &vao));
	GL_CALL(glBindVertexArray(vao));


	GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
	//4 为vao加入位置和颜色的描述信息
	//4.1 位置描述信息
	GL_CALL(glEnableVertexAttribArray(0));
	GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0));

	//4.2 颜色描述信息
	GL_CALL(glEnableVertexAttribArray(1));
	GL_CALL(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))));

	//5 扫尾工作:解绑当前vao
	glBindVertexArray(0);
}

chap07 shader

void prepareShader() {
	//1 完成vs与fs的源代码,并且装入字符串
	const char* vertexShaderSource =
		"#version 460 core\n"
		"layout (location = 0) in vec3 aPos;\n"
		"void main()\n"
		"{\n"
		"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
		"}\0";
	const char* fragmentShaderSource =
		"#version 330 core\n"
		"out vec4 FragColor;\n"
		"void main()\n"
		"{\n"
		"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
		"}\n\0";


	//2 创建Shader程序(vs、fs)
	GLuint vertex, fragment;
	vertex = glCreateShader(GL_VERTEX_SHADER);
	fragment = glCreateShader(GL_FRAGMENT_SHADER);


	//3 为shader程序输入shader代码
	glShaderSource(vertex, 1, &vertexShaderSource, NULL);
	glShaderSource(fragment, 1, &fragmentShaderSource, NULL);

	int success = 0;
	char infoLog[1024];
	//4 执行shader代码编译 
	glCompileShader(vertex);
	//检查vertex编译结果
	glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(vertex, 1024, NULL, infoLog);
		std::cout << "Error: SHADER COMPILE ERROR --VERTEX" << "\n" << infoLog << std::endl;
	}

	glCompileShader(fragment);
	//检查fragment编译结果
	glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(fragment, 1024, NULL, infoLog);
		std::cout << "Error: SHADER COMPILE ERROR --FRAGMENT" << "\n" << infoLog << std::endl;
	}

	//5 创建一个Program壳子
	GLuint program = 0;
	program = glCreateProgram();

	//6 将vs与fs编译好的结果放到program这个壳子里
	glAttachShader(program, vertex);
	glAttachShader(program, fragment);

	//7 执行program的链接操作,形成最终可执行shader程序
	glLinkProgram(program);

	//检查链接错误
	glGetProgramiv(program, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(program, 1024, NULL, infoLog);
		std::cout << "Error: SHADER LINK ERROR " << "\n" << infoLog << std::endl;
	}

	//清理
	glDeleteShader(vertex);
	glDeleteShader(fragment);
}

chap08 glDrawArray

绘制单个三角形

void render() {
	//执行opengl画布清理操作
	GL_CALL(glClear(GL_COLOR_BUFFER_BIT));

	//1 绑定当前的program
	glUseProgram(program);

	//2 绑定当前的vao
	glBindVertexArray(vao);

	//3 发出绘制指令
	glDrawArrays(GL_TRIANGLES, 0, 3);

}

Chap09 EBO

void prepareVAO() {
	//1 准备positions
	float positions[] = {
		-0.5f, -0.5f, 0.0f,
		0.5f, -0.5f, 0.0f,
		0.0f,  0.5f, 0.0f,
		0.5f,  0.5f, 0.0f,
	};

	unsigned int indices[] = {
		0, 1, 2,
		2, 1, 3
	};

	//2 VBO创建
	GLuint vbo;
	glGenBuffers(1, &vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, 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, vbo);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)0);

	//5.2 加入ebo到当前的vao
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

	glBindVertexArray(0);
}
void render() {
	//执行opengl画布清理操作
	GL_CALL(glClear(GL_COLOR_BUFFER_BIT));

	//1 绑定当前的program
	GL_CALL(glUseProgram(program));

	//2 绑定当前的vao
	GL_CALL(glBindVertexArray(vao));
	//3 发出绘制指令
//	glDrawArrays(GL_LINE_STRIP, 0, 6);
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
	glBindVertexArray(0);
}

Chap10 绘制彩色三角形

#include <iostream>
#define DEBUG

//注意:glad头文件必须在glfw引用之前引用
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <string>
#include <assert.h>//断言
#include "wrapper/checkError.h"
#include "application/Application.h"

GLuint vao, program;


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() {
	//1 完成vs与fs的源代码,并且装入字符串
	const char* vertexShaderSource =
		"#version 460 core\n"
		"layout (location = 0) in vec3 aPos;\n"
		"layout (location = 1) in vec3 aColor;\n"
		"out vec3 color;\n"
		"void main()\n"
		"{\n"
		"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
		"   color = aColor;\n"
		"}\0";
	const char* fragmentShaderSource =
		"#version 330 core\n"
		"out vec4 FragColor;\n"
		"in vec3 color;\n"
		"void main()\n"
		"{\n"
		"   FragColor = vec4(color, 1.0f);\n"
		"}\n\0";


	//2 创建Shader程序(vs、fs)
	GLuint vertex, fragment;
	vertex = glCreateShader(GL_VERTEX_SHADER);
	fragment = glCreateShader(GL_FRAGMENT_SHADER);


	//3 为shader程序输入shader代码
	glShaderSource(vertex, 1, &vertexShaderSource, NULL);
	glShaderSource(fragment, 1, &fragmentShaderSource, NULL);

	int success = 0;
	char infoLog[1024];
	//4 执行shader代码编译 
	glCompileShader(vertex);
	//检查vertex编译结果
	glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(vertex, 1024, NULL, infoLog);
		std::cout << "Error: SHADER COMPILE ERROR --VERTEX" << "\n" << infoLog << std::endl;
	}

	glCompileShader(fragment);
	//检查fragment编译结果
	glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(fragment, 1024, NULL, infoLog);
		std::cout << "Error: SHADER COMPILE ERROR --FRAGMENT" << "\n" << infoLog << std::endl;
	}

	//5 创建一个Program壳子
	program = glCreateProgram();

	//6 将vs与fs编译好的结果放到program这个壳子里
	glAttachShader(program, vertex);
	glAttachShader(program, fragment);

	//7 执行program的链接操作,形成最终可执行shader程序
	glLinkProgram(program);

	//检查链接错误
	glGetProgramiv(program, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(program, 1024, NULL, infoLog);
		std::cout << "Error: SHADER LINK ERROR " << "\n" << infoLog << std::endl;
	}

	//清理
	glDeleteShader(vertex);
	glDeleteShader(fragment);
}

void render() {
	//执行opengl画布清理操作
	GL_CALL(glClear(GL_COLOR_BUFFER_BIT));

	//1 绑定当前的program
	GL_CALL(glUseProgram(program));

	//2 绑定当前的vao
	GL_CALL(glBindVertexArray(vao));
	//3 发出绘制指令
//	glDrawArrays(GL_LINE_STRIP, 0, 6);
	glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
	glBindVertexArray(0);
}


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;
}

Chap11 Shader封装类

把shader相关代码封装至shader类,把glsl代码放进assests文件夹下的glsl文件,用数据流读取

core.h
#pragma once

//注意:glad头文件必须在glfw引用之前引用
#include <glad/glad.h>
#include <GLFW/glfw3.h>

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 };
};

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;
	}
}

chap12 动态属性获取及Uniform

fragment.glsl
#version 460 core
out vec4 FragColor;

uniform float time;

in vec3 color;
void main()
{
   float intensity = (sin(time) + 1.0) / 2.0;
   FragColor = vec4(vec3(intensity) + color, 1.0f);
}

vertex.glsl
#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

uniform float time;

out vec3 color;
void main()
{
   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
   //color = aColor;
   color = aColor * (cos(time) + 1.0) / 2.0;
}

shader.cpp
void Shader::setFloat(const std::string& name, float value) {
	//1 通过名称拿到Uniform变量的位置Location
	GLint location = GL_CALL(glGetUniformLocation(mProgram, name.c_str()));

	//2 通过Location更新Uniform变量的值
	GL_CALL(glUniform1f(location, value));
}

chap13 纹理

届ける言葉を今は育ててる
最后更新于 2024-08-14