自定义着色器类
声明
#pragma once
#include<string>
class Shaders
{
public:
Shaders(const char* vertexPath, const char* fragmentPath);
~Shaders();
std::string vertexString;
std::string fragmentString;
const char* vertexSource;
const char* fragmentSource;
unsigned int ID; // shaderProgram 的id
void use();
private:
void checkCompileErrors(unsigned int id, std::string type);
};
源文件
#include "Shaders.h"
#include<fstream>
#include<iostream>
#include<sstream>
#define GLEW_STATIC // 这个一定要加不然报错 静态链接库
#include<GL/glew.h>
#include<GLFW/glfw3.h>
Shaders::Shaders(const char* vertexPath, const char* fragmentPath ) {
std::ifstream vertexFile;
std::ifstream fragmentFile;
std::stringstream vertexSStream;
std::stringstream fragmentSStream;
vertexFile.open(vertexPath);
fragmentFile.open(fragmentPath);
vertexFile.exceptions(std::ifstream::failbit || std::ifstream::badbit);
fragmentFile.exceptions(std::ifstream::failbit || std::ifstream::badbit);
try {
if (!vertexFile.is_open() || !fragmentFile.is_open()) {
throw std::exception("open file error");
}
// 将 输入流的内容输出给 stringstream
vertexSStream << vertexFile.rdbuf();
fragmentSStream << fragmentFile.rdbuf();
vertexString = vertexSStream.str();
fragmentString = fragmentSStream.str();
vertexSource = vertexString.c_str();
fragmentSource = fragmentString.c_str();
unsigned int vertex, fragment;
// 创建这个着色器
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vertexSource, NULL);
// 编译
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fragmentSource, NULL);
// 编译
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
//创建一个着色器程序对象
ID = glCreateProgram();
// 将之前编译的着色器附加到程序对象上
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
// 用glLinkProgram链接它们
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
//删除着色器
glDeleteShader(vertex);
glDeleteShader(fragment);
}
catch (const std::exception& ex) {
std::cout << ex.what() << std::endl;
}
}
void Shaders::use() {
glUseProgram(ID);
}
void Shaders::checkCompileErrors(unsigned int id, std::string type) {
int success;
char infoLog[512];
if (type != "PROGRAM") {
glGetShaderiv(id, GL_COMPILE_STATUS,&success);
if (!success) {
glGetShaderInfoLog(id, 512, NULL, infoLog);
std::cout << "shader compile error:" << infoLog << std::endl;
}
}
else {
glGetProgramiv(id, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(id, 512, NULL, infoLog);
std::cout << "program compile error:" << infoLog << std::endl;
}
}
}
Shaders::~Shaders() {}
程序入口
#define GLEW_STATIC // 这个一定要加不然报错 静态链接库
#include<GL/glew.h>
#include<GLFW/glfw3.h>
#include<iostream>
#include "Shaders.h"
using namespace std;
void processInput(GLFWwindow);
void processInput(GLFWwindow* window) {
//如果键盘输入esc 则触发 退出
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
// 设置 要求退出
glfwSetWindowShouldClose(window, true);
}
}
// 逆时针方向绘制 默认情况下,逆时针的顶点连接顺序被定义为三角形的正
// 逆时针或顺时针都是相对于观察者方向的
float vertices[] {
-0.5f, 0.5f, 0.0f, 1.0f, 0,0, // 左上角 0
0.5f, -0.5f, 0.0f, 0,1.0f,0, // 右下角 1
0.5f, 0.5f, 0.0f, 0,0,1.0f, // 右上角 2
// 第二个三角形
//0.5f, -0.5f, 0.0f, // 右下角
-0.5f, -0.5f, 0.0f , 1.0f,0,1.0f // 左下角 3
//-0.5f, 0.5f, 0.0f // 左上角
};
// 使用索引来减小画点的开销 (未用索引缓冲对象时,每个点都需要画一次(即使重复了))
unsigned int indices[] {
0,1,2, //第一个三角形的索引
1,3,0 //第二个三角形的索引
};
int main() {
// 初始化GLFW
glfwInit();
// 提示 我们使用的版本是3.3
// 主版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
// 次版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
// 简介
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 创建一个窗口对象
GLFWwindow* window = glfwCreateWindow(800, 600, "Test window", NULL, NULL);
if (window == NULL) {
cout << "open window failed." << endl;
// 终止 glfw
glfwTerminate();
}
// 绑定window到上下文对象 创建完窗口我们就可以通知GLFW将我们窗口的上下文设置为当前线程的主上下文了
glfwMakeContextCurrent(window);
glewExperimental = true;
// GLEW_OK 0
//init GLEW
if (glewInit() != GLEW_OK) {
cout << "glew init failed." << endl;
// 终止 glfw
glfwTerminate();
return -1;
}
// OpenGL渲染窗口的尺寸大小
// glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)
glViewport(0, 0, 800, 600);
// 设置剔除 (opegl默认正面背面都显示(不剔除))
//glEnable(GL_CULL_FACE);
// 剔除背面 GL_BACK 剔除正面 GL_FRONT
//glCullFace(GL_BACK);
// 线框模式
//第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//VAO对象
unsigned int VAO;
// 生成一个VAO对象 这个方法可以生成多个 由第一个参数决定
glGenVertexArrays(1, &VAO);
// 绑定 VAO
glBindVertexArray(VAO);
unsigned int VBO; //如果多个可以用 VBO[]数组 这个方法可以生成多个 由第一个参数决定
glGenBuffers(1, &VBO);
//将新创建的缓冲绑定到 GL_ARRAY_BUFFER目标上
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// glBufferData 是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
// GL_STATIC_DRAW 数据不会或几乎不会改变。
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
Shaders* shaders = new Shaders("./vertexSource.txt", "./fragmentSource.txt");
// glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)
// 从 0号栏位 开始 将数据每三个为一组 单位为float 每次跳6*float字节 偏移为0
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
// 以顶点属性位置值作为参数,启用顶点属性;顶点属性默认是禁用的
//读取到0号栏位上
glEnableVertexAttribArray(0);
// 读取颜色属性 从 1号栏位 开始 将数据每三个为一组 单位为float 每次跳6*float字节 偏移为3个float
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float)));
// 读取到1号栏位上
glEnableVertexAttribArray(1);
//渲染循环 ,它能在我们让GLFW退出前一直保持运行。下面几行的代码就实现了一个简单的渲染循环:
//glfwWindowShouldClose 我们每次循环的开始前检查一次GLFW是否被要求退出
while (!glfwWindowShouldClose(window)) {
//自定义事件 当键盘触发esc 退出
processInput(window);
glClearColor(0.2, 0.3, 0.3, 1.0);
// GL_COLOR_BUFFER_BIT 颜色,GL_DEPTH_BUFFER_BIT 深度 和 GL_STENCIL_BUFFER_BIT 模板
// 清除前面的那一帧的颜色
glClear(GL_COLOR_BUFFER_BIT);
// 绑定 VAO
glBindVertexArray(VAO);
shaders->use();
// 画三角形 从0开始 绘制三个顶点 和VBO的顶点数据(通过VAO间接绑定)来绘制图元
//glDrawArrays(GL_TRIANGLES, 0, 6); 这个是不用索引画的
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//可以不需要这个 绑定VAO的同时也会自动绑定EBO
//glDrawElements函数从当前绑定到GL_ELEMENT_ARRAY_BUFFER目标的EBO中获取索引
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//解绑VAO
glBindVertexArray(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
// 最后终止 glfw
glfwTerminate();
return 0;
}
着色器
顶点着色器
#version 330 core
// 0号栏位读取顶点坐标
layout (location = 0) in vec3 aPos;
//1号栏位读取颜色颜色
layout (location = 1) in vec3 aColor;
out vec4 vertexColor;
void main()
{
// gl_Position 是固定的名称 用来保存 顶点坐标的
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
vertexColor = vec4(aColor.x+0.5,aColor.y,aColor.z,1.0);
}
片段着色器
#version 330 core
out vec4 FragColor;
//uniform vec4 outColor;
in vec4 vertexColor;
void main()
{
FragColor = vertexColor;
}