最近尝试手搓一个简单的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));
}
Comments NOTHING